/*
Copyright (C) 2015 William Hart
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
#include
#include
#include "flint.h"
#include "nmod_vec.h"
#include "nmod_mat.h"
#include "nmod_poly.h"
void nmod_mat_minpoly_with_gens(nmod_poly_t p, const nmod_mat_t X, ulong * P)
{
slong n = X->r, i, j, c, c1, c2, r1, r2;
ulong ** A, ** B, ** v, t, h;
slong * P1, * P2, * L1, * L2;
nmod_mat_t matA, matB, matv;
int first_poly = 1, indep = 1;
nmod_poly_t b, g;
TMP_INIT;
if (X->r != X->c)
{
flint_printf("Exception (nmod_mat_charpoly). Non-square matrix.\n");
flint_abort();
}
if (n == 0)
{
nmod_poly_one(p);
return;
}
if (n == 1)
{
nmod_poly_set_coeff_ui(p, 1, 1);
nmod_poly_set_coeff_ui(p, 0, n_negmod(X->rows[0][0], p->mod.n));
_nmod_poly_set_length(p, 2);
if (P != NULL)
P[0] = 1;
return;
}
TMP_START;
nmod_poly_init(b, p->mod.n);
nmod_poly_init(g, p->mod.n);
nmod_poly_one(p);
nmod_mat_init(matA, n + 1, 2*n + 1, p->mod.n);
nmod_mat_init(matB, n, n, p->mod.n);
nmod_mat_init(matv, n, 1, p->mod.n);
A = matA->rows;
B = matB->rows;
v = matv->rows;
L1 = (slong *) TMP_ALLOC((n + 1)*sizeof(slong));
L2 = (slong *) TMP_ALLOC(n*sizeof(slong));
P1 = (slong *) TMP_ALLOC((2*n + 1)*sizeof(slong));
P2 = (slong *) TMP_ALLOC(n*sizeof(slong));
for (i = 1; i <= n + 1; i++)
L1[i - 1] = n + i;
for (i = 1; i <= n; i++)
L2[i - 1] = n;
for (i = 1; i < n; i++)
P2[i] = -WORD(1);
P2[0] = 0;
r2 = c2 = 0;
first_poly = 1;
while (r2 < n)
{
for (i = 0; i < 2*n + 1; i++)
P1[i] = -WORD(1);
for (i = 0; i < n; i++)
{
v[i][0] = 0;
B[r2][i] = 0;
A[0][i] = 0;
}
P1[c2] = 0;
P2[c2] = r2;
v[c2][0] = 1;
B[r2][c2] = 1;
A[0][c2] = 1;
A[0][n] = 1;
if (P != NULL)
P[c2] = 1;
indep = 1;
r1 = 0;
c1 = -WORD(1);
while (c1 < n && r1 < n)
{
r1++;
r2 = indep ? r2 + 1 : r2;
nmod_mat_mul(matv, X, matv);
v = matv->rows;
for (i = 0; i < n; i++)
A[r1][i] = v[i][0];
for (i = n; i < n + r1; i++)
A[r1][i] = 0;
A[r1][n + r1] = 1;
c1 = nmod_mat_reduce_row(matA, P1, L1, r1);
if (indep && r2 < n && !first_poly)
{
for (i = 0; i < n; i++)
B[r2][i] = v[i][0];
c = nmod_mat_reduce_row(matB, P2, L2, r2);
indep = c != -WORD(1);
}
}
if (first_poly)
{
for (i = 0; i < n; i++)
P2[i] = P1[i];
r2 = r1;
}
c = -WORD(1);
for (i = c2 + 1; i < n; i++)
{
if (P2[i] == -WORD(1))
{
c = i;
break;
}
}
c2 = c;
nmod_poly_fit_length(b, r1 + 1);
h = n_invmod(A[r1][n + r1], p->mod.n);
for (i = 0; i < r1 + 1; i++)
{
t = n_mulmod2_preinv(A[r1][n + i], h, p->mod.n, p->mod.ninv);
nmod_poly_set_coeff_ui(b, i, t);
}
_nmod_poly_set_length(b, r1 + 1);
nmod_poly_gcd(g, p, b);
nmod_poly_mul(p, p, b);
nmod_poly_div(p, p, g);
if (first_poly && r2 < n)
{
for (i = 0; i < r1; i++)
{
for (j = 0; j < n; j++)
B[i][j] = A[i][j];
}
}
first_poly = 0;
}
nmod_mat_clear(matA);
nmod_mat_clear(matB);
nmod_mat_clear(matv);
nmod_poly_clear(b);
nmod_poly_clear(g);
TMP_END;
}
void nmod_mat_minpoly(nmod_poly_t p, const nmod_mat_t X)
{
nmod_mat_minpoly_with_gens(p, X, NULL);
}