/* 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 "fmpz_mat.h" #include "fmpz_mod_mat.h" #include "perm.h" int fmpz_mod_mat_is_in_howell_form(const fmpz_mod_mat_t A) { slong *pivots; slong i, j, r; int numberpivots = 0; int prevrowzero = 0; fmpz * extra_row; fmpz_t g; if (fmpz_mod_mat_is_empty(A)) return 1; pivots = flint_malloc(A->mat->r * sizeof(slong)); if (!fmpz_mat_is_zero_row(A->mat, 0)) { for (j = 0; j < A->mat->c; j++) { if (!fmpz_is_zero(fmpz_mod_mat_entry(A, 0, j))) { if (!fmpz_divisible(A->mod, fmpz_mod_mat_entry(A, 0, j))) { flint_free(pivots); return 0; } pivots[numberpivots] = j; numberpivots++; break; } } } else { prevrowzero = 1; } for (i = 1; i < A->mat->r; i++) { if (!fmpz_mat_is_zero_row(A->mat, i)) { if (prevrowzero) { flint_free(pivots); return 0; } for (j = 0; j < A->mat->c; j++) { if (!fmpz_is_zero(fmpz_mod_mat_entry(A, i, j))) { if (j <= pivots[numberpivots - 1]) { flint_free(pivots); return 0; } if (!fmpz_divisible(A->mod, fmpz_mod_mat_entry(A, i, j))) { flint_free(pivots); return 0; } pivots[numberpivots] = j; numberpivots++; j = A->mat->c; } } } else { prevrowzero = 1; } } for (i = 1; i < numberpivots; i++) { for (j = 0; j < i; j++) { if (fmpz_cmp(fmpz_mod_mat_entry(A, j, pivots[i]), fmpz_mod_mat_entry(A, i, pivots[i])) >= 0) { flint_free(pivots); return 0; } } } extra_row = _fmpz_vec_init(A->mat->c); fmpz_init(g); for (i = 0; i < numberpivots; i++) { fmpz_gcd(g, A->mod, fmpz_mod_mat_entry(A, i, pivots[i])); fmpz_divexact(g, A->mod, g); _fmpz_vec_scalar_mul_fmpz(extra_row, A->mat->rows[i], A->mat->c, g); _fmpz_vec_scalar_mod_fmpz(extra_row, extra_row, A->mat->c, A->mod); for ( j = pivots[i] + 1; j < A->mat->c; j++) { if (!fmpz_is_zero(extra_row + j)) { for ( r = i; r < numberpivots; r++) { if (pivots[r] == j) { if (fmpz_divisible(extra_row + j, fmpz_mod_mat_entry(A, r, pivots[r]))) { fmpz_divexact(g, extra_row + j, fmpz_mod_mat_entry(A, r, pivots[r])); fmpz_neg(g, g); _fmpz_vec_scalar_addmul_fmpz(extra_row, A->mat->rows[r], A->mat->c, g); } } } } } _fmpz_vec_scalar_mod_fmpz(extra_row, extra_row, A->mat->c, A->mod); if (!_fmpz_vec_is_zero(extra_row, A->mat->c)) { _fmpz_vec_clear(extra_row, A->mat->c); flint_free(pivots); fmpz_clear(g); return 0; } } _fmpz_vec_clear(extra_row, A->mat->c); flint_free(pivots); fmpz_clear(g); return 1; } int main(void) { slong i; FLINT_TEST_INIT(state); flint_printf("howell_form...."); for (i = 0; i < 10000*flint_test_multiplier(); i++) { fmpz_mod_mat_t A, B, D; fmpz_t mod; fmpz_t t, c; slong j, k, m, n, r1, r2; slong *perm; int equal; fmpz_init(mod); fmpz_init(t); fmpz_init(c); do { fmpz_randtest_unsigned(mod, state, 10); } while (fmpz_is_zero(mod)); m = n_randint(state, 20); do { n = n_randint(state, 20); } while (n > m); perm = _perm_init(2*m); fmpz_mod_mat_init(A, m, n, mod); fmpz_mod_mat_init(D, 2*m, n, mod); fmpz_mod_mat_randtest(A, state); fmpz_mod_mat_init_set(B, A); r1 = fmpz_mod_mat_howell_form(B); if (!fmpz_mod_mat_is_in_howell_form(B)) { flint_printf("FAIL (malformed Howell form)\n"); fmpz_mod_mat_print_pretty(A); flint_printf("\n\n"); fmpz_mod_mat_print_pretty(B); flint_printf("\n\n"); flint_printf("Modulus: "); fmpz_print(mod); 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++) { while (1) { fmpz_randtest_mod(c, state, mod); fmpz_gcd(t, c, mod); if (fmpz_is_one(t)) { break; } } for (k = 0; k < n; k++) fmpz_mul(fmpz_mod_mat_entry(D, perm[j], k), fmpz_mod_mat_entry(A, j, k), c); } for (j = 0; j < m; j++) { while (1) { fmpz_randtest_mod(c, state, mod); fmpz_gcd(t, c, mod); if (fmpz_is_one(t)) { break; } } for (k = 0; k < n; k++) fmpz_mul(fmpz_mod_mat_entry(D, perm[m + j], k), fmpz_mod_mat_entry(B, j, k), c); } _fmpz_mod_mat_reduce(D); r2 = fmpz_mod_mat_howell_form(D); equal = (r1 == r2); if (equal) { for (j = 0; j < r1; j++) for (k = 0; k < n; k++) equal = equal && fmpz_equal(fmpz_mod_mat_entry(B, j, k), fmpz_mod_mat_entry(D, j, k)); for (j = r1; j < 2*m; j++) for (k = 0; k < n; k++) equal = equal && fmpz_is_zero(fmpz_mod_mat_entry(D, j, k)); } if (!equal) { flint_printf("FAIL (r1 = %wd, r2 = %wd)!\n", r1, r2); fmpz_mod_mat_print_pretty(A); flint_printf("\n\n"); fmpz_mod_mat_print_pretty(B); flint_printf("\n\n"); fmpz_mod_mat_print_pretty(D); flint_printf("\n\n"); flint_printf("Modulus: "); fmpz_print(mod); abort(); } _perm_clear(perm); fmpz_mod_mat_clear(A); fmpz_mod_mat_clear(B); fmpz_mod_mat_clear(D); fmpz_clear(mod); fmpz_clear(t); fmpz_clear(c); } FLINT_TEST_CLEANUP(state); flint_printf("PASS\n"); return 0; }