/* 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 "flint.h" #include "nmod_vec.h" #include "nmod_mat.h" #include "ulong_extras.h" static __inline__ int _nmod_mat_pivot(nmod_mat_t A, slong start_row, slong col) { slong j; mp_ptr u; if (nmod_mat_entry(A, start_row, col) != 0) return 1; for (j = start_row + 1; j < A->r; j++) { if (nmod_mat_entry(A, j, col) != 0) { u = A->rows[j]; A->rows[j] = A->rows[start_row]; A->rows[start_row] = u; return -1; } } return 0; } static void _n_ppio(mp_ptr ppi, mp_ptr ppo, mp_limb_t a, mp_limb_t b) { mp_limb_t c, n, g; c = n_gcd(a, b); n = a/c; g = n_gcd(c, n); while( g != 1 ) { c = c * g; n = n/g; g = n_gcd(c, n); } *ppi = c; *ppo = n; } static mp_limb_t _n_stab(mp_limb_t a, mp_limb_t b, nmod_t N) { mp_limb_t g, s, t; g = n_gcd(a, b); b = n_gcd(g, N.n); _n_ppio(&s, &t, N.n/b, a/b); return t; } static mp_limb_t _n_unit(mp_limb_t a, nmod_t N) { mp_limb_t g, s, l, d; g = n_gcdinv(&s, a, N.n); if (g == 1) { return s; } else { l = N.n/g; d = _n_stab(s, l, N); return nmod_add(s, nmod_mul(d, l, N), N); } } /* test wether q*a = b mod N has a solution */ static int _n_is_divisible(mp_ptr q, mp_limb_t b, mp_limb_t a, nmod_t N) { mp_limb_t e, g; g = n_gcdinv(&e, a, N.n); if (( b % g ) == 0) { *q = nmod_mul(e, b/g, N); return 1; } return 0; } void nmod_mat_strong_echelon_form(nmod_mat_t A) { mp_limb_t s, t, u, v, q, t1, t2, g; slong m, n, row, col, i, k, l; mp_limb_t **r; nmod_t mod; mp_ptr extra_row; if (nmod_mat_is_empty(A)) return; n = A->r; m = A->c; r = A->rows; mod = A->mod; extra_row = _nmod_vec_init(m); row = col = 0; while (row < n && col < m) { if (_nmod_mat_pivot(A, row, col) == 0) { col++; continue; } for (i = row + 1; i < n; i++) { if (nmod_mat_entry(A, i, col) == 0) { continue; } if (_n_is_divisible(&s, nmod_mat_entry(A, i, col), nmod_mat_entry(A, row, col), mod)) { for (k = col; k < m; k++) { t1 = nmod_sub(nmod_mat_entry(A, i, k), nmod_mul(s, nmod_mat_entry(A, row, k), mod), mod); nmod_mat_entry(A, i, k) = t1; } } else { if (nmod_mat_entry(A, row, col) >= nmod_mat_entry(A, i, col)) { g = n_xgcd(&s, &t, nmod_mat_entry(A, row, col), nmod_mat_entry(A, i, col)); } else { g = n_xgcd(&t, &s, nmod_mat_entry(A, i, col), nmod_mat_entry(A, row, col)); } /* now g = a*x - b*y a,b < x < mod.n */ t = nmod_neg(t, mod); u = (nmod_mat_entry(A, i, col))/g; u = nmod_neg(u, mod); v = (nmod_mat_entry(A, row, col))/g; /* now g = a*x + b*y and 0 = sv - tu = 1 modulo mod.n */ for (k = col; k < m; k++) { t1 = nmod_add(nmod_mul(s, nmod_mat_entry(A, row, k), mod), nmod_mul(t, nmod_mat_entry(A, i, k), mod), mod); t2 = nmod_add(nmod_mul(u, nmod_mat_entry(A, row, k), mod), nmod_mul(v, nmod_mat_entry(A, i, k), mod), mod); nmod_mat_entry(A, row, k) = t1; nmod_mat_entry(A, i, k) = t2; } } } row++; col++; } for (col = 0; col < m; col++) { if (nmod_mat_entry(A, col, col) != 0) { u = _n_unit(nmod_mat_entry(A, col, col), mod); for (k = col; k < m; k++) { nmod_mat_entry(A, col, k) = nmod_mul(u, nmod_mat_entry(A, col, k), mod); } for (row = 0; row < col ; row++) { q = nmod_mat_entry(A, row, col)/nmod_mat_entry(A, col, col); for (l = row; l< m; l++) { s = nmod_sub(nmod_mat_entry(A, row, l), nmod_mul(q, nmod_mat_entry(A, col, l), mod), mod); nmod_mat_entry(A, row, l) = s; } } g = n_gcd(mod.n, nmod_mat_entry(A, col, col)); if (g == 1) { continue; } g = mod.n/g; _nmod_vec_scalar_mul_nmod(extra_row, r[col], m, g, mod); } else { _nmod_vec_set(extra_row, r[col], m); } for (row = col + 1; row < m; row++) { if(nmod_mat_entry(A, row, row) >= extra_row[row]) { g = n_xgcd(&s, &t, nmod_mat_entry(A, row, row), extra_row[row]); } else { g = n_xgcd(&t, &s, extra_row[row], nmod_mat_entry(A, row, row)); } if (g == 0) { continue; } t = nmod_neg(t, mod); u = extra_row[row]/g; u = nmod_neg(u, mod); v = (nmod_mat_entry(A, row, row))/g; /* now g = a*x + b*y and 0 = sv - tu = 1 modulo mod.n */ for (k = row; k < m; k++) { t1 = nmod_add(nmod_mul(s, nmod_mat_entry(A, row, k), mod), nmod_mul(t, extra_row[k], mod), mod); t2 = nmod_add(nmod_mul(u, nmod_mat_entry(A, row, k), mod), nmod_mul(v, extra_row[k], mod), mod); nmod_mat_entry(A, row, k) = t1; extra_row[k] = t2; } } } _nmod_vec_clear(extra_row); }