/*
Copyright (C) 2015 Tommy Hofmann
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
#include "flint.h"
#include "nmod_vec.h"
#include "nmod_mat.h"
#include "ulong_extras.h"
#include "perm.h"
int
nmod_mat_is_in_howell_form(const nmod_mat_t A)
{
slong *pivots;
slong i, j, r;
int numberpivots = 0;
int prevrowzero = 0;
mp_ptr extra_row;
mp_limb_t g;
if (nmod_mat_is_zero(A))
return 1;
pivots = flint_malloc(A->r * sizeof(slong));
if (!nmod_mat_is_zero_row(A, 0))
{
for (j = 0; j < A->c; j++)
{
if (nmod_mat_entry(A, 0, j))
{
if ((A->mod).n % nmod_mat_entry(A, 0, j))
{
flint_free(pivots);
return 0;
}
pivots[numberpivots] = j;
numberpivots++;
break;
}
}
}
else
{
prevrowzero = 1;
}
for (i = 1; i < A->r; i++)
{
if (!nmod_mat_is_zero_row(A, i))
{
if (prevrowzero)
{
flint_free(pivots);
return 0;
}
for (j = 0; j < A->c; j++)
{
if (nmod_mat_entry(A, i, j))
{
if (j <= pivots[numberpivots - 1])
{
flint_free(pivots);
return 0;
}
if ((A->mod).n % nmod_mat_entry(A, i, j))
{
flint_free(pivots);
return 0;
}
pivots[numberpivots] = j;
numberpivots++;
j = A->c;
}
}
}
else
{
prevrowzero = 1;
}
}
for (i = 1; i < numberpivots; i++)
{
for (j = 0; j < i; j++)
{
if (nmod_mat_entry(A, j, pivots[i]) >= nmod_mat_entry(A, i, pivots[i]))
{
flint_free(pivots);
return 0;
}
}
}
extra_row = _nmod_vec_init(A->c);
for (i = 0; i < numberpivots; i++)
{
g = n_gcd(A->mod.n, nmod_mat_entry(A, i, pivots[i]));
if (g == 1)
{
continue;
}
g = A->mod.n/g;
_nmod_vec_scalar_mul_nmod(extra_row, A->rows[i], A->c, g, A->mod);
for ( j = pivots[i] + 1; j < A->c; j++)
{
if (extra_row[j])
{
for ( r = i; r < numberpivots; r++)
{
if (pivots[r] == j)
{
if(!(extra_row[j] % nmod_mat_entry(A, r, pivots[r])))
{
g = extra_row[j]/nmod_mat_entry(A, r, pivots[r]);
_nmod_vec_scalar_addmul_nmod(extra_row, A->rows[r],
A->c, nmod_neg(g, A->mod), A->mod);
}
}
}
}
}
if (!_nmod_vec_is_zero(extra_row, A->c))
{
_nmod_vec_clear(extra_row);
flint_free(pivots);
return 0;
}
}
_nmod_vec_clear(extra_row);
flint_free(pivots);
return 1;
}
int
main(void)
{
slong i;
FLINT_TEST_INIT(state);
flint_printf("howell_form....");
for (i = 0; i < 10000*flint_test_multiplier(); i++)
{
nmod_mat_t A, B, D;
mp_limb_t mod;
slong j, k, m, n, r1, r2;
slong *perm;
int equal;
mp_limb_t c;
mod = n_randtest_not_zero(state);
m = n_randint(state, 20);
do { n = n_randint(state, 20); } while (n > m);
perm = _perm_init(2*m);
nmod_mat_init(A, m, n, mod);
nmod_mat_init(D, 2*m, n, mod);
nmod_mat_randtest(A, state);
nmod_mat_init_set(B, A);
r1 = nmod_mat_howell_form(B);
if (!nmod_mat_is_in_howell_form(B))
{
flint_printf("FAIL (malformed Howell form)\n");
nmod_mat_print_pretty(A); flint_printf("\n\n");
nmod_mat_print_pretty(B); flint_printf("\n\n");
abort();
}
_perm_randtest(perm, 2 * m, state);
/* Concatenate the original matrix with the Howell form, scramble the rows,
and check that the Howell form is the same */
for (j = 0; j < m; j++)
{
do { c = n_randint(state, mod); } while ( n_gcd(c, mod) != 1);
for (k = 0; k < n; k++)
nmod_mat_entry(D, perm[j], k) =
nmod_mul(nmod_mat_entry(A, j, k), c, A->mod);
}
for (j = 0; j < m; j++)
{
do { c = n_randint(state, mod); } while ( n_gcd(c, mod) != 1);
for (k = 0; k < n; k++)
nmod_mat_entry(D, perm[m + j], k) =
nmod_mul(nmod_mat_entry(B, j, k), c, A->mod);
}
r2 = nmod_mat_howell_form(D);
equal = (r1 == r2);
if (equal)
{
for (j = 0; j < r1; j++)
for (k = 0; k < n; k++)
equal = equal && (nmod_mat_entry(B, j, k) ==
nmod_mat_entry(D, j, k));
for (j = r1; j < 2*m; j++)
for (k = 0; k < n; k++)
equal = equal && (nmod_mat_entry(D, j, k) == 0);
}
if (!equal)
{
flint_printf("FAIL (r1 = %wd, r2 = %wd)!\n", r1, r2);
nmod_mat_print_pretty(A); flint_printf("\n\n");
nmod_mat_print_pretty(B); flint_printf("\n\n");
nmod_mat_print_pretty(D); flint_printf("\n\n");
abort();
}
_perm_clear(perm);
nmod_mat_clear(A);
nmod_mat_clear(B);
nmod_mat_clear(D);
}
FLINT_TEST_CLEANUP(state);
flint_printf("PASS\n");
return 0;
}