/*
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_charpoly_danilevsky(nmod_poly_t p, const nmod_mat_t M)
{
slong n = M->r, i, j, k;
ulong ** A;
ulong * V, * W, * T;
ulong h;
nmod_poly_t b;
nmod_mat_t M2;
int num_limbs;
TMP_INIT;
if (M->r != M->c)
{
flint_printf("Exception (nmod_mat_charpoly_danilevsky). 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(M->rows[0][0], p->mod.n));
_nmod_poly_set_length(p, 2);
return;
}
TMP_START;
i = 1;
num_limbs = _nmod_vec_dot_bound_limbs(n, p->mod);
nmod_poly_one(p);
nmod_poly_init(b, p->mod.n);
nmod_mat_init_set(M2, M);
V = (ulong *) TMP_ALLOC(n*sizeof(ulong));
W = (ulong *) TMP_ALLOC(n*sizeof(ulong));
T = (ulong *) TMP_ALLOC(n*sizeof(ulong));
A = M2->rows;
while (i < n)
{
h = A[n - i][n - i - 1];
while (h == 0)
{
k = 1;
while (k < n - i && A[n - i][n - i - k - 1] == 0)
k++;
if (k == n - i)
{
nmod_poly_fit_length(b, i + 1);
nmod_poly_set_coeff_ui(b, i, 1);
for (k = 1; k <= i; k++)
nmod_poly_set_coeff_ui(b, k - 1, n_negmod(A[n - i][n - k], p->mod.n));
_nmod_poly_set_length(b, i + 1);
nmod_poly_mul(p, p, b);
n -= i;
i = 1;
if (n == 1)
{
nmod_poly_set_coeff_ui(b, 1, 1);
nmod_poly_set_coeff_ui(b, 0, n_negmod(A[0][0], p->mod.n));
_nmod_poly_set_length(b, 2);
nmod_poly_mul(p, p, b);
goto cleanup;
}
} else
{
ulong * ptr;
ulong t;
ptr = A[n - i - k - 1];
A[n - i - k - 1] = A[n - i - 1];
A[n - i - 1] = ptr;
for (j = 1; j <= n - i + 1; j++)
{
t = A[j - 1][n - i - k - 1];
A[j - 1][n - i - k - 1] = A[j - 1][n - i - 1];
A[j - 1][n - i - 1] = t;
}
}
h = A[n - i][n - i - 1];
}
h = n_invmod(n_negmod(h, p->mod.n), p->mod.n);
for (j = 1; j <= n; j++)
{
V[j - 1] = n_mulmod2_preinv(A[n - i][j - 1], h, p->mod.n, p->mod.ninv);
W[j - 1] = A[n - i][j - 1];
}
h = n_negmod(h, p->mod.n);
for (j = 1; j <= n - i; j++)
{
for (k = 1; k <= n - i - 1; k++)
NMOD_ADDMUL(A[j - 1][k - 1], A[j - 1][n - i - 1], V[k - 1], p->mod);
for (k = n - i + 1; k <= n; k++)
NMOD_ADDMUL(A[j - 1][k - 1], A[j - 1][n - i - 1], V[k - 1], p->mod);
A[j - 1][n - i - 1] = n_mulmod2_preinv(A[j - 1][n - i - 1], h, p->mod.n, p->mod.ninv);
}
for (j = 1; j <= n - i - 1; j++)
{
for (k = 1; k <= n - i; k++)
T[k - 1] = A[k - 1][j - 1];
A[n - i - 1][j - 1] = _nmod_vec_dot(T, W, n - i, p->mod, num_limbs);
}
for (j = n - i; j <= n - 1; j++)
{
for (k = 1; k <= n - i; k++)
T[k - 1] = A[k - 1][j - 1];
A[n - i - 1][j - 1] = n_addmod(_nmod_vec_dot(T, W, n - i, p->mod, num_limbs), W[j], p->mod.n);
}
for (k = 1; k <= n - i; k++)
T[k - 1] = A[k - 1][j - 1];
A[n - i - 1][n - 1] = _nmod_vec_dot(T, W, n - i, p->mod, num_limbs);
i++;
}
nmod_poly_fit_length(b, n + 1);
nmod_poly_set_coeff_ui(b, n, 1);
for (i = 1; i <= n; i++)
nmod_poly_set_coeff_ui(b, i - 1, n_negmod(A[0][n - i], p->mod.n));
_nmod_poly_set_length(b, n + 1);
nmod_poly_mul(p, p, b);
cleanup:
nmod_mat_clear(M2);
nmod_poly_clear(b);
TMP_END;
}