diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2616d0669f..77bb965ba9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,16 +59,15 @@ endif()
# Find sources
set(BUILD_DIRS
- aprcl ulong_extras long_extras perm fmpz fmpz_vec fmpz_poly
- fmpq_poly fmpz_mat fmpz_lll mpfr_vec mpfr_mat mpf_vec mpf_mat nmod_vec nmod_poly
- nmod_poly_factor arith mpn_extras nmod_mat fmpq fmpq_vec fmpq_mat padic
- fmpz_poly_q fmpz_poly_mat nmod_poly_mat fmpz_mod_poly fmpz_mod_mat
+ aprcl ulong_extras long_extras perm fmpz fmpz_vec fmpz_sparse_vec fmpz_poly
+ fmpq_poly fmpz_mat fmpz_sparse_mat fmpz_mod_mat fmpz_lll mpfr_vec mpfr_mat mpf_vec mpf_mat nmod_vec nmod_sparse_vec nmod_poly
+ nmod_poly_factor arith mpn_extras nmod_mat nmod_sparse_mat fmpq fmpq_vec fmpq_mat padic
+ fmpz_poly_q fmpz_poly_mat nmod_poly_mat fmpz_mod_poly
fmpz_mod_poly_factor fmpz_factor fmpz_poly_factor fft qsieve
double_extras d_vec d_mat padic_poly padic_mat qadic
- fq fq_vec fq_mat fq_poly fq_poly_factor
- fq_nmod fq_nmod_vec fq_nmod_mat fq_nmod_poly fq_nmod_poly_factor
- fq_zech fq_zech_vec fq_zech_mat fq_zech_poly fq_zech_poly_factor
- fq_default fq_default_mat fq_default_poly fq_default_poly_factor
+ fq fq_vec fq_sparse_vec fq_mat fq_sparse_mat fq_poly fq_poly_factor
+ fq_nmod fq_nmod_vec fq_nmod_sparse_vec fq_nmod_mat fq_nmod_sparse_mat fq_nmod_poly fq_nmod_poly_factor
+ fq_zech fq_zech_vec fq_zech_sparse_vec fq_zech_mat fq_zech_sparse_mat fq_zech_poly fq_zech_poly_factor
thread_pool fmpz_mod fmpz_mod_vec n_poly
mpoly fmpz_mpoly fmpq_mpoly nmod_mpoly fq_nmod_mpoly fmpz_mod_mpoly
fmpz_mpoly_factor fmpq_mpoly_factor nmod_mpoly_factor fmpz_mod_mpoly_factor
@@ -77,14 +76,14 @@ set(BUILD_DIRS
)
set(TEMPLATE_DIRS
- fq_vec_templates fq_mat_templates fq_poly_templates
+ fq_vec_templates fq_sparse_vec_templates fq_mat_templates fq_sparse_mat_templates fq_poly_templates
fq_poly_factor_templates fq_templates
)
set(SOURCES
printf.c fprintf.c sprintf.c scanf.c fscanf.c sscanf.c clz_tab.c
memory_manager.c version.c profiler.c thread_support.c exception.c
- hashmap.c inlines.c fmpz/fmpz.c
+ heap.c hashmap.c inlines.c fmpz/fmpz.c
)
if (MSVC)
@@ -97,7 +96,7 @@ endif()
set(HEADERS
NTL-interface.h flint.h longlong.h flint-config.h gmpcompat.h fft_tuning.h
- fmpz-conversions.h profiler.h templates.h exception.h hashmap.h
+ fmpz-conversions.h profiler.h templates.h exception.h heap.h hashmap.h
)
foreach (build_dir IN LISTS BUILD_DIRS TEMPLATE_DIRS)
diff --git a/Makefile.in b/Makefile.in
index e2ad7de956..4de9334a99 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -6,15 +6,15 @@ QUIET_AR = @echo ' ' AR ' ' $@;
AT=@
-BUILD_DIRS = aprcl ulong_extras long_extras perm fmpz fmpz_vec fmpz_poly \
- fmpq_poly fmpz_mat fmpz_lll mpfr_vec mpfr_mat mpf_vec mpf_mat nmod_vec nmod_poly \
- nmod_poly_factor arith mpn_extras nmod_mat fmpq fmpq_vec fmpq_mat padic \
+BUILD_DIRS = aprcl ulong_extras long_extras perm fmpz fmpz_vec fmpz_sparse_vec fmpz_poly \
+ fmpq_poly fmpz_mat fmpz_sparse_mat fmpz_lll mpfr_vec mpfr_mat mpf_vec mpf_mat nmod_vec nmod_sparse_vec nmod_poly \
+ nmod_poly_factor arith mpn_extras nmod_mat nmod_sparse_mat fmpq fmpq_vec fmpq_mat padic \
fmpz_poly_q fmpz_poly_mat nmod_poly_mat fmpz_mod_poly \
fmpz_mod_poly_factor fmpz_factor fmpz_poly_factor fft qsieve \
double_extras d_vec d_mat padic_poly padic_mat qadic \
- fq fq_vec fq_mat fq_poly fq_poly_factor fq_embed \
- fq_nmod fq_nmod_vec fq_nmod_mat fq_nmod_poly fq_nmod_poly_factor fq_nmod_embed \
- fq_zech fq_zech_vec fq_zech_mat fq_zech_poly fq_zech_poly_factor fq_zech_embed \
+ fq fq_vec fq_sparse_vec fq_mat fq_sparse_mat fq_poly fq_poly_factor fq_embed \
+ fq_nmod fq_nmod_vec fq_nmod_sparse_vec fq_nmod_mat fq_nmod_sparse_mat fq_nmod_poly fq_nmod_poly_factor fq_nmod_embed \
+ fq_zech fq_zech_vec fq_zech_sparse_vec fq_zech_mat fq_zech_sparse_mat fq_zech_poly fq_zech_poly_factor fq_zech_embed \
fmpz_mod_mat mpoly fmpz_mpoly fmpq_mpoly nmod_mpoly fq_nmod_mpoly \
thread_pool fmpz_mod fmpz_mod_vec fmpz_mod_mpoly fmpz_mod_mpoly_factor \
n_poly fmpz_mpoly_factor fmpq_mpoly_factor nmod_mpoly_factor \
@@ -27,10 +27,10 @@ TEMPLATE_DIRS = fq_vec_templates fq_mat_templates fq_poly_templates \
export
-SOURCES = printf.c fprintf.c sprintf.c scanf.c fscanf.c sscanf.c clz_tab.c memory_manager.c version.c profiler.c thread_support.c exception.c hashmap.c inlines.c
+SOURCES = printf.c fprintf.c sprintf.c scanf.c fscanf.c sscanf.c clz_tab.c memory_manager.c version.c profiler.c thread_support.c exception.c heap.c hashmap.c inlines.c
LIB_SOURCES = $(wildcard $(patsubst %, %/*.c, $(BUILD_DIRS))) $(patsubst %, %/*.c, $(TEMPLATE_DIRS))
-HEADERS = $(patsubst %, %.h, $(BUILD_DIRS)) NTL-interface.h flint.h longlong.h flint-config.h gmpcompat.h fft_tuning.h fmpz-conversions.h profiler.h templates.h exception.h hashmap.h thread_support.h $(patsubst %, %.h, $(TEMPLATE_DIRS))
+HEADERS = $(patsubst %, %.h, $(BUILD_DIRS)) NTL-interface.h flint.h longlong.h flint-config.h gmpcompat.h fft_tuning.h fmpz-conversions.h profiler.h templates.h exception.h heap.h hashmap.h thread_support.h $(patsubst %, %.h, $(TEMPLATE_DIRS))
OBJS = $(patsubst %.c, build/%.o, $(SOURCES))
LIB_OBJS = $(patsubst %, build/%/*.o, $(BUILD_DIRS))
diff --git a/config.h.in b/config.h.in
index d6e68ca591..9baa1db0e2 100644
--- a/config.h.in
+++ b/config.h.in
@@ -8,7 +8,7 @@
#cmakedefine01 FLINT_WANT_ASSERT
/* Define if you cpu_set_t in sched.h */
-#cmakedefine01 FLINT_USES_CPUSET
+#cmakedefine01 HAVE_CPU_SET_T
#cmakedefine01 FLINT_USES_PTHREAD
diff --git a/doc/source/fq_nmod_sparse_mat.rst b/doc/source/fq_nmod_sparse_mat.rst
new file mode 100644
index 0000000000..ab7f04ac78
--- /dev/null
+++ b/doc/source/fq_nmod_sparse_mat.rst
@@ -0,0 +1,295 @@
+.. _fq-nmod-sparse-mat:
+
+**fq_nmod_sparse_mat.h** -- sparse matrixs over finite fields (word-size characteristic)
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_nmod_sparse_mat_t
+
+ Holds an array of (possibly empty) sparse vectors corresponding to rows in
+ the matrix
+
+Memory management
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_init(fq_nmod_sparse_mat_t M, slong rows, slong cols, const fq_nmod_ctx_t ctx)
+
+ Initializes an empty sparse matrix ``M`` with given number of rows and columns
+
+.. function:: void fq_nmod_sparse_mat_clear(fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Clears the entries of the matrix ``M`` and frees the space allocated for it
+
+.. function:: void fq_nmod_sparse_mat_swap(fq_nmod_sparse_mat_t M1, fq_nmod_sparse_mat_t M2, const fq_nmod_ctx_t ctx)
+
+ Swaps two matrices ``M1`` and ``M2`` (no reallocaton)
+
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_zero(fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Sets matrix ``M`` to zero (the empty sparse matrix)
+
+.. function:: void fq_nmod_sparse_mat_one(fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Sets matrix ``M`` to identity matrix (based on its number of rows)
+
+.. function:: void fq_nmod_sparse_mat_set(fq_nmod_sparse_mat_t N, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Makes ``N`` a (deep) copy of ``M``
+
+.. function:: void fq_nmod_sparse_mat_from_entries(fq_nmod_sparse_mat_t M, slong *rows, slong *inds, fq_nmod_struct *vals, slong nnz, const fq_nmod_ctx_t ctx)
+
+ Constructs matrix ``M`` from a given sequence of ``rows``, ``cols``, and
+ corresponding ``vals``, all of length ``nnz``, assumes sorted by rows
+ with no duplicate (row, col) indices
+
+.. function:: void fq_nmod_sparse_mat_append_col(fq_nmod_sparse_mat_t M, const fq_nmod_struct *v, const fq_nmod_ctx_t ctx)
+
+ Add a dense column to the right of the matrix
+
+.. function:: void fq_nmod_sparse_mat_append_row(fq_nmod_sparse_mat_t M, const fq_nmod_sparse_vec_t v, const fq_nmod_ctx_t ctx)
+
+ Add a sparse row to the bottom of the matrix
+
+
+Conversion to/from dense matrix
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_from_dense(fq_nmod_sparse_mat_t M, const fq_nmod_mat_t dM, const fq_nmod_ctx_t ctx)
+
+ Converts the dense matrix ``dM`` to a sparse matrix ``M``
+
+.. function:: void fq_nmod_sparse_mat_to_dense(fq_nmod_mat_t dM, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Converts the sparse matrix ``M`` to a dense matrix ``dM``
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_window_init(fq_nmod_sparse_mat_t window, const fq_nmod_sparse_mat_t M, slong r1, slong c1, slong r2, slong c2, const fq_nmod_ctx_t ctx)
+
+ Constructs a window on the sparse matrix ``M`` between rows ``r1`` and ``r2``
+ and cols ``c1`` and ``c2`` (valid as long as original matrix remains unmodified)
+
+.. function:: void fq_nmod_sparse_mat_window_clear(fq_nmod_sparse_mat_t window, const fq_nmod_ctx_t ctx)
+
+ Clears a window
+
+.. function:: void fq_nmod_sparse_mat_concat_horizontal(fq_nmod_sparse_mat_t B, const fq_nmod_sparse_mat_t M1, const fq_nmod_sparse_mat_t M2, const fq_nmod_ctx_t ctx)
+
+ Horizontally concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_nmod_sparse_mat_concat_vertical(fq_nmod_sparse_mat_t B, const fq_nmod_sparse_mat_t M1, const fq_nmod_sparse_mat_t M2, const fq_nmod_ctx_t ctx)
+
+ Vertically concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_nmod_sparse_mat_split_horizontal(fq_nmod_sparse_mat_t M1, fq_nmod_sparse_mat_t M2, const fq_nmod_sparse_mat_t B, slong c, const fq_nmod_ctx_t ctx)
+
+ Splits ``B`` horizontally into two submatrices ``M1`` and ``M2``, dividing at column ``c``
+
+.. function:: void fq_nmod_sparse_mat_split_vertical(fq_nmod_sparse_mat_t M1, fq_nmod_sparse_mat_t M2, const fq_nmod_sparse_mat_t B, slong r, const fq_nmod_ctx_t ctx)
+
+ Splits ``B`` vertically into two submatrices ``M1`` and ``M2``, dividing at row ``r``
+
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_permute_cols(fq_nmod_sparse_mat_t M, slong *Q, const fq_nmod_ctx_t ctx)
+
+ Permutes the columns indices of ``M`` according to ``Q``, and re-sorts each row
+
+.. function:: void fq_nmod_sparse_mat_permute_rows(fq_nmod_sparse_mat_t M, slong *P, const fq_nmod_ctx_t ctx)
+
+ Permutes the rows of ``M`` according to ``P``
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_nmod_sparse_mat_randtest(fq_nmod_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, const fq_nmod_ctx_t ctx)
+
+ Makes ``M`` a sparse matrix with between ``min_nnz`` and ``max_nnz`` nonzero
+ entries per row, with individual entries generated by fq_nmod_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_print_pretty(const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Prints the matrix ``M`` to ``stdout`` in a human-readable format
+
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_is_zero(fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Checks if the given matrix ``M`` is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_nmod_sparse_mat_equal(const fq_nmod_sparse_mat_t M1, const fq_nmod_sparse_mat_t M2, slong ioff, const fq_nmod_ctx_t ctx)
+
+ Checks if ``M1`` equals ``M2``, returning `1` if so and `0` otherwise
+
+
+Transpose
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_transpose(fq_nmod_sparse_mat_t N, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Transposes ``M`` into the matrix ``N`` (must have swapped rows and columns)
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_mat_neg(fq_nmod_sparse_mat_t N, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Sets ``N`` to the negation of ``M``
+
+.. function:: void fq_nmod_sparse_mat_scalar_mul_fq_nmod(fq_nmod_sparse_mat_t N, const fq_nmod_sparse_mat_t M, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``N`` to the scalar multiple of ``M`` by ``c``
+
+.. function:: void fq_nmod_sparse_mat_add(fq_nmod_sparse_mat_t O, const fq_nmod_sparse_mat_t M, const fq_nmod_sparse_mat_t N, const fq_nmod_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``N``
+
+.. function:: void fq_nmod_sparse_mat_sub(fq_nmod_sparse_mat_t O, const fq_nmod_sparse_mat_t M, const fq_nmod_sparse_mat_t N, const fq_nmod_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N``
+
+.. function:: void fq_nmod_sparse_mat_scalar_addmul_fq_nmod(fq_nmod_sparse_mat_t O, const fq_nmod_sparse_mat_t M, const fq_nmod_sparse_mat_t N, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``c` times ``N``
+
+.. function:: void fq_nmod_sparse_mat_scalar_submul_fq_nmod(fq_nmod_sparse_mat_t O, const fq_nmod_sparse_mat_t M, const fq_nmod_sparse_mat_t N, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N` times ``v``
+
+.. function:: void fq_nmod_sparse_mat_mul_vec(fq_nmod_struct *y, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *x, const fq_nmod_ctx_t ctx)
+
+ Sets ``y`` to the product of ``M`` and ``x``
+
+.. function:: void fq_nmod_sparse_mat_mul_mat(fq_nmod_mat_t Y, const fq_nmod_sparse_mat_t M, const fq_nmod_mat_t X, const fq_nmod_ctx_t ctx)
+
+ Sets ``Y`` to the product of ``M`` and ``X``
+
+.. function:: slong fq_nmod_sparse_mat_inv(fq_nmod_sparse_mat_t N, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Sets ``N`` to the "inverse" of ``M``, i.e., the matrix such that NM is
+ in reduced row-echelon form
+
+
+Decomposition/reduction
+--------------------------------------------------------------------------------
+
+.. function:: slong fq_nmod_sparse_mat_lu(slong *P, slong *Q, fq_nmod_sparse_mat_t L, fq_nmod_sparse_mat_t U, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Computes the decomposition PMQ = LU for a given sparse matrix ``M``, where
+ ``P`` is a row permutation, ``Q`` is a column permutation, ``L``is a lower
+ triangular matrix, and ``U`` is an upper triangular matrix
+
+.. function:: void fq_nmod_sparse_mat_rref(fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Applies row reduction to put ``M`` in reduced row echelon form (in place)
+
+Solving
+--------------------------------------------------------------------------------
+
+.. function:: int fq_nmod_sparse_mat_solve_lu(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use LU decomposition to find
+ a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_solve_rref(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the reduced row-echelon
+ form to find a vector ``x`` such that Mx = b, returns `1` if successful and
+ `0` if not
+
+.. function:: int fq_nmod_sparse_mat_solve_lanczos(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Lanczos algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_solve_wiedemann(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Wiedemann algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_solve_block_lanczos(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Lanczos
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_solve_block_wiedemann(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, const fq_nmod_struct *b, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Wiedemann
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+Nullvector and nullspace computation
+--------------------------------------------------------------------------------
+
+.. function:: int fq_nmod_sparse_mat_nullvector_lanczos(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_nullvector_wiedemann(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_nullvector_block_lanczos(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_nullvector_block_wiedemann(fq_nmod_struct *x, const fq_nmod_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_nmod_sparse_mat_nullspace_rref(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the reduced row echelon form to construct the
+ nullspace ``X`` of M (initialized by this function), returns the nullity
+
+.. function:: int fq_nmod_sparse_mat_nullspace_lu(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the LU decomposition to construct the nullspace ``X``
+ of M (initialized by this function), returns the nullity
+
+.. function:: int fq_nmod_sparse_mat_nullspace_lanczos(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_nmod_sparse_mat_nullspace_wiedemann(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_nmod_sparse_mat_nullspace_block_lanczos(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_nmod_sparse_mat_nullspace_block_wiedemann(fq_nmod_mat_t X, const fq_nmod_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_nmod_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
diff --git a/doc/source/fq_nmod_sparse_vec.rst b/doc/source/fq_nmod_sparse_vec.rst
new file mode 100644
index 0000000000..3a96e1e780
--- /dev/null
+++ b/doc/source/fq_nmod_sparse_vec.rst
@@ -0,0 +1,176 @@
+.. _fq-nmod-sparse-vec:
+
+**fq_nmod_sparse_vec.h** -- sparse vectors over finite fields (word-size characteristic)
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_nmod_sparse_entry_t
+
+ Holds a pair (ind, val), where ind (of type slong) is an index into the
+ vector and val (of type fq_nmod_t) is the value at that index
+
+.. type:: fq_nmod_sparse_vec_t
+
+ Holds an array of nonzero entries (of type sparse_entry_struct *) of
+ specified length nnz, sorted by ind
+
+Memory management
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_nmod_sparse_vec_init(fq_nmod_sparse_vec_t vec, const fq_nmod_ctx_t ctx)
+
+ Initializes an empty sparse vector (no allocation)
+
+.. function:: void fq_nmod_sparse_vec_clear(fq_nmod_sparse_vec_t vec, slong len, const fq_nmod_ctx_t ctx)
+
+ Clears the entries of vec and frees the space allocated for it
+
+.. function:: void fq_nmod_sparse_vec_swap(fq_nmod_sparse_vec_t vec1, fq_nmod_sparse_vec_t vec2, const fq_nmod_ctx_t ctx)
+
+ Swaps two vectors (no reallocaton)
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_zero(fq_nmod_sparse_vec_t vec, const fq_nmod_ctx_t ctx)
+
+ Sets vec to zero (the empty sparse vector)
+
+.. function:: void fq_nmod_sparse_vec_one(fq_nmod_sparse_vec_t vec, slong ind, const fq_nmod_ctx_t ctx)
+
+ Sets vec to the ind-th basis vector (single one in position ind)
+
+.. function:: void fq_nmod_sparse_vec_set(fq_nmod_sparse_vec_t dst, fq_nmod_sparse_vec_t src, const fq_nmod_ctx_t ctx)
+
+ Makes dst a (deep) copy of src
+
+.. function:: void fq_nmod_sparse_vec_set_entry(fq_nmod_sparse_vec_t vec, slong ind, const fq_nmod_t val, const fq_nmod_ctx_t ctx)
+
+ Sets the value at index ind to val, either replacing an existing value or extending
+ the array of entries
+
+.. function:: void _fq_nmod_sparse_vec_from_entries(fq_nmod_sparse_vec_t vec, slong *inds, fq_nmod_struct *vals, slong nnz, const fq_nmod_ctx_t ctx)
+
+ Constructs vec from a given sequence of indices and associated values, both of length nnz.
+ Assumes no duplicate indices
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_is_zero(fq_nmod_sparse_vec_t vec, const fq_nmod_ctx_t ctx)
+
+ Checks if the given vector is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_nmod_sparse_vec_equal(const fq_nmod_sparse_vec_t vec1, const fq_nmod_sparse_vec_t vec2, slong ioff, const fq_nmod_ctx_t ctx)
+
+ Checks if vec1 equals vec2 (with s specified column offset ioff), returning
+ `1` if so and `0` otherwise
+
+Indexing
+--------------------------------------------------------------------------------
+
+.. function:: fq_nmod_t * fq_nmod_sparse_vec_at(fq_nmod_sparse_vec_t vec, slong ind, const fq_nmod_ctx_t ctx)
+
+ Returns a pointer to the value at the index ind (or NULL if index not found)
+
+
+Conversion to/from dense vector
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_from_dense(fq_nmod_sparse_vec_t dst, const fq_nmod_struct *src, slong len, const fq_nmod_ctx_t ctx)
+
+ Converts the dense vector src of length len to a sparse vector
+
+.. function:: void fq_nmod_sparse_vec_to_dense(fq_nmod_struct *dst, const fq_nmod_sparse_vec_t src, slong len, const fq_nmod_ctx_t ctx)
+
+ Converts the sparse vector src to a dense vector of length len
+
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_window_init(fq_nmod_sparse_vec_t window, const fq_nmod_sparse_vec_t vec, slong i1, slong i2, const fq_nmod_ctx_t ctx)
+
+ Constructs a window on a the sparse vector vec between indices i1 and i2
+ Note that window is only valid as long as original vector remains unmodified
+
+.. function:: void fq_nmod_sparse_vec_window_clear(fq_nmod_sparse_vec_t window, const fq_nmod_ctx_t ctx)
+
+ Clears a window (for safety only)
+
+.. function:: void fq_nmod_sparse_vec_concat(fq_nmod_sparse_vec_t res, const fq_nmod_sparse_vec_t vec1, const fq_nmod_sparse_vec_t vec2, slong len1, const fq_nmod_ctx_t ctx)
+
+ Concatenates two vectors vec1 and vec2 into res, with indices of vec2
+ offset by len1
+
+.. function:: void fq_nmod_sparse_vec_split(fq_nmod_sparse_vec_t res1, fq_nmod_sparse_vec_t res1, const fq_nmod_sparse_vec_t vec, slong ind, const fq_nmod_ctx_t ctx)
+
+ Splits vec into two vectors res1 and res2, with res1 containing all entries
+ below index ind and res2 containing the rest
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_permute_inds(fq_nmod_sparse_vec_t vec, slong *P, const fq_nmod_ctx_t ctx)
+
+ Permutes the indices of vec according to P, and resorts
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_nmod_sparse_vec_randtest(fq_nmod_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, const fq_nmod_ctx_t ctx)
+
+ Makes vec a sparse vector with nnz nonzero entries uniformly distributed
+ between 0 and len - 1, with individual entries generated by fq_nmod_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_print_pretty(const fq_nmod_sparse_vec_t vec, slong ioff, slong maxi, const fq_nmod_ctx_t ctx)
+
+ Prints the vector of given length to ``stdout`` in a human-readable format
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_nmod_sparse_vec_neg(fq_nmod_sparse_vec_t v, const fq_nmod_sparse_vec_t u, const fq_nmod_ctx_t ctx)
+
+ Sets ``v`` to the negation of ``u``
+
+.. function:: void fq_nmod_sparse_vec_scalar_mul_fq_nmod(fq_nmod_sparse_vec_t v, const fq_nmod_sparse_vec_t u, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``v`` to the scalar multiple of ``u`` by ``c``
+
+.. function:: void fq_nmod_sparse_vec_add(fq_nmod_sparse_vec_t w, const fq_nmod_sparse_vec_t u, const fq_nmod_sparse_vec_t v, const fq_nmod_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``v``
+
+.. function:: void fq_nmod_sparse_vec_sub(fq_nmod_sparse_vec_t w, const fq_nmod_sparse_vec_t u, const fq_nmod_sparse_vec_t v, const fq_nmod_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``v``
+
+.. function:: void fq_nmod_sparse_vec_scalar_addmul_fq_nmod(fq_nmod_sparse_vec_t w, const fq_nmod_sparse_vec_t u, const fq_nmod_sparse_vec_t v, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``c` times ``v``
+
+.. function:: void fq_nmod_sparse_vec_scalar_addmul_fq_nmod(fq_nmod_sparse_vec_t w, const fq_nmod_sparse_vec_t u, const fq_nmod_sparse_vec_t v, const fq_nmod_t c, const fq_nmod_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``c` times ``v``
+
+.. function:: void fq_nmod_sparse_vec_dot(fq_nmod_t ret, const fq_nmod_sparse_vec_t u, const fq_nmod_sparse_vec_t v, const fq_nmod_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of ``u`` and ``v``
+
+.. function:: void fq_nmod_sparse_vec_dot_dense(fq_nmod_t ret, const fq_nmod_sparse_vec_t u, const fq_nmod_struct * v, const fq_nmod_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of (``u``, ``v``)
diff --git a/doc/source/fq_sparse_mat.rst b/doc/source/fq_sparse_mat.rst
new file mode 100644
index 0000000000..45484a8bc9
--- /dev/null
+++ b/doc/source/fq_sparse_mat.rst
@@ -0,0 +1,295 @@
+.. _fq-sparse-mat:
+
+**fq_sparse_mat.h** -- sparse matrixs over finite fields
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_sparse_mat_t
+
+ Holds an array of (possibly empty) sparse vectors corresponding to rows in
+ the matrix
+
+Memory management
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_init(fq_sparse_mat_t M, slong rows, slong cols, const fq_ctx_t ctx)
+
+ Initializes an empty sparse matrix ``M`` with given number of rows and columns
+
+.. function:: void fq_sparse_mat_clear(fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Clears the entries of the matrix ``M`` and frees the space allocated for it
+
+.. function:: void fq_sparse_mat_swap(fq_sparse_mat_t M1, fq_sparse_mat_t M2, const fq_ctx_t ctx)
+
+ Swaps two matrices ``M1`` and ``M2`` (no reallocaton)
+
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_zero(fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Sets matrix ``M`` to zero (the empty sparse matrix)
+
+.. function:: void fq_sparse_mat_one(fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Sets matrix ``M`` to identity matrix (based on its number of rows)
+
+.. function:: void fq_sparse_mat_set(fq_sparse_mat_t N, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Makes ``N`` a (deep) copy of ``M``
+
+.. function:: void fq_sparse_mat_from_entries(fq_sparse_mat_t M, slong *rows, slong *inds, fq_struct *vals, slong nnz, const fq_ctx_t ctx)
+
+ Constructs matrix ``M`` from a given sequence of ``rows``, ``cols``, and
+ corresponding ``vals``, all of length ``nnz``, assumes sorted by rows
+ with no duplicate (row, col) indices
+
+.. function:: void fq_sparse_mat_append_col(fq_sparse_mat_t M, const fq_struct *v, const fq_ctx_t ctx)
+
+ Add a dense column to the right of the matrix
+
+.. function:: void fq_sparse_mat_append_row(fq_sparse_mat_t M, const fq_sparse_vec_t v, const fq_ctx_t ctx)
+
+ Add a sparse row to the bottom of the matrix
+
+
+Conversion to/from dense matrix
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_from_dense(fq_sparse_mat_t M, const fq_mat_t dM, const fq_ctx_t ctx)
+
+ Converts the dense matrix ``dM`` to a sparse matrix ``M``
+
+.. function:: void fq_sparse_mat_to_dense(fq_mat_t dM, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Converts the sparse matrix ``M`` to a dense matrix ``dM``
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_window_init(fq_sparse_mat_t window, const fq_sparse_mat_t M, slong r1, slong c1, slong r2, slong c2, const fq_ctx_t ctx)
+
+ Constructs a window on the sparse matrix ``M`` between rows ``r1`` and ``r2``
+ and cols ``c1`` and ``c2`` (valid as long as original matrix remains uzechified)
+
+.. function:: void fq_sparse_mat_window_clear(fq_sparse_mat_t window, const fq_ctx_t ctx)
+
+ Clears a window
+
+.. function:: void fq_sparse_mat_concat_horizontal(fq_sparse_mat_t B, const fq_sparse_mat_t M1, const fq_sparse_mat_t M2, const fq_ctx_t ctx)
+
+ Horizontally concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_sparse_mat_concat_vertical(fq_sparse_mat_t B, const fq_sparse_mat_t M1, const fq_sparse_mat_t M2, const fq_ctx_t ctx)
+
+ Vertically concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_sparse_mat_split_horizontal(fq_sparse_mat_t M1, fq_sparse_mat_t M2, const fq_sparse_mat_t B, slong c, const fq_ctx_t ctx)
+
+ Splits ``B`` horizontally into two submatrices ``M1`` and ``M2``, dividing at column ``c``
+
+.. function:: void fq_sparse_mat_split_vertical(fq_sparse_mat_t M1, fq_sparse_mat_t M2, const fq_sparse_mat_t B, slong r, const fq_ctx_t ctx)
+
+ Splits ``B`` vertically into two submatrices ``M1`` and ``M2``, dividing at row ``r``
+
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_permute_cols(fq_sparse_mat_t M, slong *Q, const fq_ctx_t ctx)
+
+ Permutes the columns indices of ``M`` according to ``Q``, and re-sorts each row
+
+.. function:: void fq_sparse_mat_permute_rows(fq_sparse_mat_t M, slong *P, const fq_ctx_t ctx)
+
+ Permutes the rows of ``M`` according to ``P``
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_sparse_mat_randtest(fq_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, const fq_ctx_t ctx)
+
+ Makes ``M`` a sparse matrix with between ``min_nnz`` and ``max_nnz`` nonzero
+ entries per row, with individual entries generated by fq_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_print_pretty(const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Prints the matrix ``M`` to ``stdout`` in a human-readable format
+
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_is_zero(fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Checks if the given matrix ``M`` is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_sparse_mat_equal(const fq_sparse_mat_t M1, const fq_sparse_mat_t M2, slong ioff, const fq_ctx_t ctx)
+
+ Checks if ``M1`` equals ``M2``, returning `1` if so and `0` otherwise
+
+
+Transpose
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_transpose(fq_sparse_mat_t N, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Transposes ``M`` into the matrix ``N`` (must have swapped rows and columns)
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_neg(fq_sparse_mat_t N, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Sets ``N`` to the negation of ``M``
+
+.. function:: void fq_sparse_mat_scalar_mul_fq(fq_sparse_mat_t N, const fq_sparse_mat_t M, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``N`` to the scalar multiple of ``M`` by ``c``
+
+.. function:: void fq_sparse_mat_add(fq_sparse_mat_t O, const fq_sparse_mat_t M, const fq_sparse_mat_t N, const fq_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``N``
+
+.. function:: void fq_sparse_mat_sub(fq_sparse_mat_t O, const fq_sparse_mat_t M, const fq_sparse_mat_t N, const fq_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N``
+
+.. function:: void fq_sparse_mat_scalar_addmul_fq(fq_sparse_mat_t O, const fq_sparse_mat_t M, const fq_sparse_mat_t N, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``c` times ``N``
+
+.. function:: void fq_sparse_mat_scalar_submul_fq(fq_sparse_mat_t O, const fq_sparse_mat_t M, const fq_sparse_mat_t N, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N` times ``v``
+
+.. function:: void fq_sparse_mat_mul_vec(fq_struct *y, const fq_sparse_mat_t M, const fq_struct *x, const fq_ctx_t ctx)
+
+ Sets ``y`` to the product of ``M`` and ``x``
+
+.. function:: void fq_sparse_mat_mul_mat(fq_mat_t Y, const fq_sparse_mat_t M, const fq_mat_t X, const fq_ctx_t ctx)
+
+ Sets ``Y`` to the product of ``M`` and ``X``
+
+.. function:: slong fq_sparse_mat_inv(fq_sparse_mat_t N, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Sets ``N`` to the "inverse" of ``M``, i.e., the matrix such that NM is
+ in reduced row-echelon form
+
+
+Decomposition/reduction
+--------------------------------------------------------------------------------
+
+.. function:: slong fq_sparse_mat_lu(slong *P, slong *Q, fq_sparse_mat_t L, fq_sparse_mat_t U, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Computes the decomposition PMQ = LU for a given sparse matrix ``M``, where
+ ``P`` is a row permutation, ``Q`` is a column permutation, ``L``is a lower
+ triangular matrix, and ``U`` is an upper triangular matrix
+
+.. function:: void fq_sparse_mat_rref(fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Applies row reduction to put ``M`` in reduced row echelon form (in place)
+
+Solving
+--------------------------------------------------------------------------------
+
+.. function:: int fq_sparse_mat_solve_lu(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use LU decomposition to find
+ a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_solve_rref(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the reduced row-echelon
+ form to find a vector ``x`` such that Mx = b, returns `1` if successful and
+ `0` if not
+
+.. function:: int fq_sparse_mat_solve_lanczos(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Lanczos algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_solve_wiedemann(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Wiedemann algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_solve_block_lanczos(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Lanczos
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_solve_block_wiedemann(fq_struct *x, const fq_sparse_mat_t M, const fq_struct *b, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Wiedemann
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+Nullvector and nullspace computation
+--------------------------------------------------------------------------------
+
+.. function:: int fq_sparse_mat_nullvector_lanczos(fq_struct *x, const fq_sparse_mat_t M, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_nullvector_wiedemann(fq_struct *x, const fq_sparse_mat_t M, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_nullvector_block_lanczos(fq_struct *x, const fq_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_nullvector_block_wiedemann(fq_struct *x, const fq_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_sparse_mat_nullspace_rref(fq_mat_t X, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the reduced row echelon form to construct the
+ nullspace ``X`` of M (initialized by this function), returns the nullity
+
+.. function:: int fq_sparse_mat_nullspace_lu(fq_mat_t X, const fq_sparse_mat_t M, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the LU decomposition to construct the nullspace ``X``
+ of M (initialized by this function), returns the nullity
+
+.. function:: int fq_sparse_mat_nullspace_lanczos(fq_mat_t X, const fq_sparse_mat_t M, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_sparse_mat_nullspace_wiedemann(fq_mat_t X, const fq_sparse_mat_t M, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_sparse_mat_nullspace_block_lanczos(fq_mat_t X, const fq_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_sparse_mat_nullspace_block_wiedemann(fq_mat_t X, const fq_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
diff --git a/doc/source/fq_sparse_vec.rst b/doc/source/fq_sparse_vec.rst
new file mode 100644
index 0000000000..3be0271f33
--- /dev/null
+++ b/doc/source/fq_sparse_vec.rst
@@ -0,0 +1,176 @@
+.. _fq-sparse-vec:
+
+**fq_sparse_vec.h** -- sparse vectors over finite fields
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_sparse_entry_t
+
+ Holds a pair (ind, val), where ind (of type slong) is an index into the
+ vector and val (of type fq_t) is the value at that index
+
+.. type:: fq_sparse_vec_t
+
+ Holds an array of nonzero entries (of type sparse_entry_struct *) of
+ specified length nnz, sorted by ind
+
+Memory management
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_sparse_vec_init(fq_sparse_vec_t vec, const fq_ctx_t ctx)
+
+ Initializes an empty sparse vector (no allocation)
+
+.. function:: void fq_sparse_vec_clear(fq_sparse_vec_t vec, slong len, const fq_ctx_t ctx)
+
+ Clears the entries of vec and frees the space allocated for it
+
+.. function:: void fq_sparse_vec_swap(fq_sparse_vec_t vec1, fq_sparse_vec_t vec2, const fq_ctx_t ctx)
+
+ Swaps two vectors (no reallocaton)
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_zero(fq_sparse_vec_t vec, const fq_ctx_t ctx)
+
+ Sets vec to zero (the empty sparse vector)
+
+.. function:: void fq_sparse_vec_one(fq_sparse_vec_t vec, slong ind, const fq_ctx_t ctx)
+
+ Sets vec to the ind-th basis vector (single one in position ind)
+
+.. function:: void fq_sparse_vec_set(fq_sparse_vec_t dst, fq_sparse_vec_t src, const fq_ctx_t ctx)
+
+ Makes dst a (deep) copy of src
+
+.. function:: void fq_sparse_vec_set_entry(fq_sparse_vec_t vec, slong ind, const fq_t val, const fq_ctx_t ctx)
+
+ Sets the value at index ind to val, either replacing an existing value or extending
+ the array of entries
+
+.. function:: void _fq_sparse_vec_from_entries(fq_sparse_vec_t vec, slong *inds, fq_struct *vals, slong nnz, const fq_ctx_t ctx)
+
+ Constructs vec from a given sequence of indices and associated values, both of length nnz.
+ Assumes no duplicate indices
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_is_zero(fq_sparse_vec_t vec, const fq_ctx_t ctx)
+
+ Checks if the given vector is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_sparse_vec_equal(const fq_sparse_vec_t vec1, const fq_sparse_vec_t vec2, slong ioff, const fq_ctx_t ctx)
+
+ Checks if vec1 equals vec2 (with s specified column offset ioff), returning
+ `1` if so and `0` otherwise
+
+Indexing
+--------------------------------------------------------------------------------
+
+.. function:: fq_t * fq_sparse_vec_at(const fq_sparse_vec_t vec, slong ind, const fq_ctx_t ctx)
+
+ Returns a pointer to the value at the index ind (or NULL if index not found)
+
+
+Conversion to/from dense vector
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_from_dense(fq_sparse_vec_t dst, const fq_struct *src, slong len, const fq_ctx_t ctx)
+
+ Converts the dense vector src of length len to a sparse vector
+
+.. function:: void fq_sparse_vec_to_dense(fq_struct *dst, const fq_sparse_vec_t src, slong len, const fq_ctx_t ctx)
+
+ Converts the sparse vector src to a dense vector of length len
+
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_window_init(fq_sparse_vec_t window, const fq_sparse_vec_t vec, slong i1, slong i2, const fq_ctx_t ctx)
+
+ Constructs a window on a the sparse vector vec between indices i1 and i2
+ Note that window is only valid as long as original vector remains unmodified
+
+.. function:: void fq_sparse_vec_window_clear(fq_sparse_vec_t window, const fq_ctx_t ctx)
+
+ Clears a window (for safety only)
+
+.. function:: void fq_sparse_vec_concat(fq_sparse_vec_t res, const fq_sparse_vec_t vec1, const fq_sparse_vec_t vec2, slong len1, const fq_ctx_t ctx)
+
+ Concatenates two vectors vec1 and vec2 into res, with indices of vec2
+ offset by len1
+
+.. function:: void fq_sparse_vec_split(fq_sparse_vec_t res1, fq_sparse_vec_t res1, const fq_sparse_vec_t vec, slong ind, const fq_ctx_t ctx)
+
+ Splits vec into two vectors res1 and res2, with res1 containing all entries
+ below index ind and res2 containing the rest
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_permute_inds(fq_sparse_vec_t vec, slong *P, const fq_ctx_t ctx)
+
+ Permutes the indices of vec according to P, and resorts
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_sparse_vec_randtest(fq_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, const fq_ctx_t ctx)
+
+ Makes vec a sparse vector with nnz nonzero entries uniformly distributed
+ between 0 and len - 1, with individual entries generated by fq_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_print_pretty(const fq_sparse_vec_t vec, slong ioff, slong maxi, const fq_ctx_t ctx)
+
+ Prints the vector of given length to ``stdout`` in a human-readable format
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_vec_neg(fq_sparse_vec_t v, const fq_sparse_vec_t u, const fq_ctx_t ctx)
+
+ Sets ``v`` to the negation of ``u``
+
+.. function:: void fq_sparse_vec_scalar_mul_fq(fq_sparse_vec_t v, const fq_sparse_vec_t u, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``v`` to the scalar multiple of ``u`` by ``c``
+
+.. function:: void fq_sparse_vec_add(fq_sparse_vec_t w, const fq_sparse_vec_t u, const fq_sparse_vec_t v, const fq_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``v``
+
+.. function:: void fq_sparse_vec_sub(fq_sparse_vec_t w, const fq_sparse_vec_t u, const fq_sparse_vec_t v, const fq_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``v``
+
+.. function:: void fq_sparse_vec_scalar_addmul_fq(fq_sparse_vec_t w, const fq_sparse_vec_t u, const fq_sparse_vec_t v, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``c` times ``v``
+
+.. function:: void fq_sparse_vec_scalar_addmul_fq(fq_sparse_vec_t w, const fq_sparse_vec_t u, const fq_sparse_vec_t v, const fq_t c, const fq_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``c` times ``v``
+
+.. function:: void fq_sparse_vec_dot(fq_t ret, const fq_sparse_vec_t u, const fq_sparse_vec_t v, const fq_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of ``u`` and ``v``
+
+.. function:: void fq_sparse_vec_dot_dense(fq_t ret, const fq_sparse_vec_t u, const fq_struct * v, const fq_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of (``u``, ``v``)
diff --git a/doc/source/fq_zech_sparse_mat.rst b/doc/source/fq_zech_sparse_mat.rst
new file mode 100644
index 0000000000..f7f5db6f4a
--- /dev/null
+++ b/doc/source/fq_zech_sparse_mat.rst
@@ -0,0 +1,295 @@
+.. _fq-zech-sparse-mat:
+
+**fq_zech_sparse_mat.h** -- sparse matrixs over finite fields (Zech logarithm representation)
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_zech_sparse_mat_t
+
+ Holds an array of (possibly empty) sparse vectors corresponding to rows in
+ the matrix
+
+Memory management
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_init(fq_zech_sparse_mat_t M, slong rows, slong cols, const fq_zech_ctx_t ctx)
+
+ Initializes an empty sparse matrix ``M`` with given number of rows and columns
+
+.. function:: void fq_zech_sparse_mat_clear(fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Clears the entries of the matrix ``M`` and frees the space allocated for it
+
+.. function:: void fq_zech_sparse_mat_swap(fq_zech_sparse_mat_t M1, fq_zech_sparse_mat_t M2, const fq_zech_ctx_t ctx)
+
+ Swaps two matrices ``M1`` and ``M2`` (no reallocaton)
+
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_zero(fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Sets matrix ``M`` to zero (the empty sparse matrix)
+
+.. function:: void fq_zech_sparse_mat_one(fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Sets matrix ``M`` to identity matrix (based on its number of rows)
+
+.. function:: void fq_zech_sparse_mat_set(fq_zech_sparse_mat_t N, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Makes ``N`` a (deep) copy of ``M``
+
+.. function:: void fq_zech_sparse_mat_from_entries(fq_zech_sparse_mat_t M, slong *rows, slong *inds, fq_zech_struct *vals, slong nnz, const fq_zech_ctx_t ctx)
+
+ Constructs matrix ``M`` from a given sequence of ``rows``, ``cols``, and
+ corresponding ``vals``, all of length ``nnz``, assumes sorted by rows
+ with no duplicate (row, col) indices
+
+.. function:: void fq_zech_sparse_mat_append_col(fq_zech_sparse_mat_t M, const fq_zech_struct *v, const fq_zech_ctx_t ctx)
+
+ Add a dense column to the right of the matrix
+
+.. function:: void fq_zech_sparse_mat_append_row(fq_zech_sparse_mat_t M, const fq_zech_sparse_vec_t v, const fq_zech_ctx_t ctx)
+
+ Add a sparse row to the bottom of the matrix
+
+
+Conversion to/from dense matrix
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_from_dense(fq_zech_sparse_mat_t M, const fq_zech_mat_t dM, const fq_zech_ctx_t ctx)
+
+ Converts the dense matrix ``dM`` to a sparse matrix ``M``
+
+.. function:: void fq_zech_sparse_mat_to_dense(fq_zech_mat_t dM, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Converts the sparse matrix ``M`` to a dense matrix ``dM``
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_window_init(fq_zech_sparse_mat_t window, const fq_zech_sparse_mat_t M, slong r1, slong c1, slong r2, slong c2, const fq_zech_ctx_t ctx)
+
+ Constructs a window on the sparse matrix ``M`` between rows ``r1`` and ``r2``
+ and cols ``c1`` and ``c2`` (valid as long as original matrix remains uzechified)
+
+.. function:: void fq_zech_sparse_mat_window_clear(fq_zech_sparse_mat_t window, const fq_zech_ctx_t ctx)
+
+ Clears a window
+
+.. function:: void fq_zech_sparse_mat_concat_horizontal(fq_zech_sparse_mat_t B, const fq_zech_sparse_mat_t M1, const fq_zech_sparse_mat_t M2, const fq_zech_ctx_t ctx)
+
+ Horizontally concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_zech_sparse_mat_concat_vertical(fq_zech_sparse_mat_t B, const fq_zech_sparse_mat_t M1, const fq_zech_sparse_mat_t M2, const fq_zech_ctx_t ctx)
+
+ Vertically concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void fq_zech_sparse_mat_split_horizontal(fq_zech_sparse_mat_t M1, fq_zech_sparse_mat_t M2, const fq_zech_sparse_mat_t B, slong c, const fq_zech_ctx_t ctx)
+
+ Splits ``B`` horizontally into two submatrices ``M1`` and ``M2``, dividing at column ``c``
+
+.. function:: void fq_zech_sparse_mat_split_vertical(fq_zech_sparse_mat_t M1, fq_zech_sparse_mat_t M2, const fq_zech_sparse_mat_t B, slong r, const fq_zech_ctx_t ctx)
+
+ Splits ``B`` vertically into two submatrices ``M1`` and ``M2``, dividing at row ``r``
+
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_permute_cols(fq_zech_sparse_mat_t M, slong *Q, const fq_zech_ctx_t ctx)
+
+ Permutes the columns indices of ``M`` according to ``Q``, and re-sorts each row
+
+.. function:: void fq_zech_sparse_mat_permute_rows(fq_zech_sparse_mat_t M, slong *P, const fq_zech_ctx_t ctx)
+
+ Permutes the rows of ``M`` according to ``P``
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_zech_sparse_mat_randtest(fq_zech_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, const fq_zech_ctx_t ctx)
+
+ Makes ``M`` a sparse matrix with between ``min_nnz`` and ``max_nnz`` nonzero
+ entries per row, with individual entries generated by fq_zech_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_print_pretty(const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Prints the matrix ``M`` to ``stdout`` in a human-readable format
+
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_is_zero(fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Checks if the given matrix ``M`` is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_zech_sparse_mat_equal(const fq_zech_sparse_mat_t M1, const fq_zech_sparse_mat_t M2, slong ioff, const fq_zech_ctx_t ctx)
+
+ Checks if ``M1`` equals ``M2``, returning `1` if so and `0` otherwise
+
+
+Transpose
+--------------------------------------------------------------------------------
+
+.. function:: void fq_sparse_mat_transpose(fq_zech_sparse_mat_t N, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Transposes ``M`` into the matrix ``N`` (must have swapped rows and columns)
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_mat_neg(fq_zech_sparse_mat_t N, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Sets ``N`` to the negation of ``M``
+
+.. function:: void fq_zech_sparse_mat_scalar_mul_fq_zech(fq_zech_sparse_mat_t N, const fq_zech_sparse_mat_t M, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``N`` to the scalar multiple of ``M`` by ``c``
+
+.. function:: void fq_zech_sparse_mat_add(fq_zech_sparse_mat_t O, const fq_zech_sparse_mat_t M, const fq_zech_sparse_mat_t N, const fq_zech_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``N``
+
+.. function:: void fq_zech_sparse_mat_sub(fq_zech_sparse_mat_t O, const fq_zech_sparse_mat_t M, const fq_zech_sparse_mat_t N, const fq_zech_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N``
+
+.. function:: void fq_zech_sparse_mat_scalar_addmul_fq_zech(fq_zech_sparse_mat_t O, const fq_zech_sparse_mat_t M, const fq_zech_sparse_mat_t N, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``O`` to the sum of ``M`` and ``c` times ``N``
+
+.. function:: void fq_zech_sparse_mat_scalar_submul_fq_zech(fq_zech_sparse_mat_t O, const fq_zech_sparse_mat_t M, const fq_zech_sparse_mat_t N, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``O`` to the difference of ``M`` and ``N` times ``v``
+
+.. function:: void fq_zech_sparse_mat_mul_vec(fq_zech_struct *y, const fq_zech_sparse_mat_t M, const fq_zech_struct *x, const fq_zech_ctx_t ctx)
+
+ Sets ``y`` to the product of ``M`` and ``x``
+
+.. function:: void fq_zech_sparse_mat_mul_mat(fq_zech_mat_t Y, const fq_zech_sparse_mat_t M, const fq_zech_mat_t X, const fq_zech_ctx_t ctx)
+
+ Sets ``Y`` to the product of ``M`` and ``X``
+
+.. function:: slong fq_zech_sparse_mat_inv(fq_zech_sparse_mat_t N, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Sets ``N`` to the "inverse" of ``M``, i.e., the matrix such that NM is
+ in reduced row-echelon form
+
+
+Decomposition/reduction
+--------------------------------------------------------------------------------
+
+.. function:: slong fq_zech_sparse_mat_lu(slong *P, slong *Q, fq_zech_sparse_mat_t L, fq_zech_sparse_mat_t U, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Computes the decomposition PMQ = LU for a given sparse matrix ``M``, where
+ ``P`` is a row permutation, ``Q`` is a column permutation, ``L``is a lower
+ triangular matrix, and ``U`` is an upper triangular matrix
+
+.. function:: void fq_zech_sparse_mat_rref(fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Applies row reduction to put ``M`` in reduced row echelon form (in place)
+
+Solving
+--------------------------------------------------------------------------------
+
+.. function:: int fq_zech_sparse_mat_solve_lu(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use LU decomposition to find
+ a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_solve_rref(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the reduced row-echelon
+ form to find a vector ``x`` such that Mx = b, returns `1` if successful and
+ `0` if not
+
+.. function:: int fq_zech_sparse_mat_solve_lanczos(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Lanczos algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_solve_wiedemann(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use the Wiedemann algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_solve_block_lanczos(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Lanczos
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_solve_block_wiedemann(fq_zech_struct *x, const fq_zech_sparse_mat_t M, const fq_zech_struct *b, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Wiedemann
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+Nullvector and nullspace computation
+--------------------------------------------------------------------------------
+
+.. function:: int fq_zech_sparse_mat_nullvector_lanczos(fq_zech_struct *x, const fq_zech_sparse_mat_t M, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_nullvector_wiedemann(fq_zech_struct *x, const fq_zech_sparse_mat_t M, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_nullvector_block_lanczos(fq_zech_struct *x, const fq_zech_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_nullvector_block_wiedemann(fq_zech_struct *x, const fq_zech_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int fq_zech_sparse_mat_nullspace_rref(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the reduced row echelon form to construct the
+ nullspace ``X`` of M (initialized by this function), returns the nullity
+
+.. function:: int fq_zech_sparse_mat_nullspace_lu(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the LU decomposition to construct the nullspace ``X``
+ of M (initialized by this function), returns the nullity
+
+.. function:: int fq_zech_sparse_mat_nullspace_lanczos(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_zech_sparse_mat_nullspace_wiedemann(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_zech_sparse_mat_nullspace_block_lanczos(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
+.. function:: int fq_zech_sparse_mat_nullspace_block_wiedemann(fq_zech_mat_t X, const fq_zech_sparse_mat_t M, slong block_size, flint_rand_t state, const fq_zech_ctx_t ctx)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
diff --git a/doc/source/fq_zech_sparse_vec.rst b/doc/source/fq_zech_sparse_vec.rst
new file mode 100644
index 0000000000..d1b85b94fd
--- /dev/null
+++ b/doc/source/fq_zech_sparse_vec.rst
@@ -0,0 +1,176 @@
+.. _fq-zech-sparse-vec:
+
+**fq_zech_sparse_vec.h** -- sparse vectors over finite fields (Zech logarithm representation)
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: fq_zech_sparse_entry_t
+
+ Holds a pair (ind, val), where ind (of type slong) is an index into the
+ vector and val (of type fq_zech_t) is the value at that index
+
+.. type:: fq_zech_sparse_vec_t
+
+ Holds an array of nonzero entries (of type sparse_entry_struct *) of
+ specified length nnz, sorted by ind
+
+Memory management
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_zech_sparse_vec_init(fq_zech_sparse_vec_t vec, const fq_zech_ctx_t ctx)
+
+ Initializes an empty sparse vector (no allocation)
+
+.. function:: void fq_zech_sparse_vec_clear(fq_zech_sparse_vec_t vec, slong len, const fq_zech_ctx_t ctx)
+
+ Clears the entries of vec and frees the space allocated for it
+
+.. function:: void fq_zech_sparse_vec_swap(fq_zech_sparse_vec_t vec1, fq_zech_sparse_vec_t vec2, const fq_zech_ctx_t ctx)
+
+ Swaps two vectors (no reallocaton)
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_zero(fq_zech_sparse_vec_t vec, const fq_zech_ctx_t ctx)
+
+ Sets vec to zero (the empty sparse vector)
+
+.. function:: void fq_zech_sparse_vec_one(fq_zech_sparse_vec_t vec, slong ind, const fq_zech_ctx_t ctx)
+
+ Sets vec to the ind-th basis vector (single one in position ind)
+
+.. function:: void fq_zech_sparse_vec_set(fq_zech_sparse_vec_t dst, fq_zech_sparse_vec_t src, const fq_zech_ctx_t ctx)
+
+ Makes dst a (deep) copy of src
+
+.. function:: void fq_zech_sparse_vec_set_entry(fq_zech_sparse_vec_t vec, slong ind, const fq_zech_t val, const fq_zech_ctx_t ctx)
+
+ Sets the value at index ind to val, either replacing an existing value or extending
+ the array of entries
+
+.. function:: void _fq_zech_sparse_vec_from_entries(fq_zech_sparse_vec_t vec, slong *inds, fq_zech_struct *vals, slong nnz, const fq_zech_ctx_t ctx)
+
+ Constructs vec from a given sequence of indices and associated values, both of length nnz.
+ Assumes no duplicate indices
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_is_zero(fq_zech_sparse_vec_t vec, const fq_zech_ctx_t ctx)
+
+ Checks if the given vector is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void fq_zech_sparse_vec_equal(const fq_zech_sparse_vec_t vec1, const fq_zech_sparse_vec_t vec2, slong ioff, const fq_zech_ctx_t ctx)
+
+ Checks if vec1 equals vec2 (with s specified column offset ioff), returning
+ `1` if so and `0` otherwise
+
+Indexing
+--------------------------------------------------------------------------------
+
+.. function:: fq_zech_t * fq_zech_sparse_vec_at(fq_zech_sparse_vec_t vec, slong ind, const fq_zech_ctx_t ctx)
+
+ Returns a pointer to the value at the index ind (or NULL if index not found)
+
+
+Conversion to/from dense vector
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_from_dense(fq_zech_sparse_vec_t dst, const fq_zech_struct *src, slong len, const fq_zech_ctx_t ctx)
+
+ Converts the dense vector src of length len to a sparse vector
+
+.. function:: void fq_zech_sparse_vec_to_dense(fq_zech_struct *dst, const fq_zech_sparse_vec_t src, slong len, const fq_zech_ctx_t ctx)
+
+ Converts the sparse vector src to a dense vector of length len
+
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_window_init(fq_zech_sparse_vec_t window, const fq_zech_sparse_vec_t vec, slong i1, slong i2, const fq_zech_ctx_t ctx)
+
+ Constructs a window on a the sparse vector vec between indices i1 and i2
+ Note that window is only valid as long as original vector remains uzechified
+
+.. function:: void fq_zech_sparse_vec_window_clear(fq_zech_sparse_vec_t window, const fq_zech_ctx_t ctx)
+
+ Clears a window (for safety only)
+
+.. function:: void fq_zech_sparse_vec_concat(fq_zech_sparse_vec_t res, const fq_zech_sparse_vec_t vec1, const fq_zech_sparse_vec_t vec2, slong len1, const fq_zech_ctx_t ctx)
+
+ Concatenates two vectors vec1 and vec2 into res, with indices of vec2
+ offset by len1
+
+.. function:: void fq_zech_sparse_vec_split(fq_zech_sparse_vec_t res1, fq_zech_sparse_vec_t res1, const fq_zech_sparse_vec_t vec, slong ind, const fq_zech_ctx_t ctx)
+
+ Splits vec into two vectors res1 and res2, with res1 containing all entries
+ below index ind and res2 containing the rest
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_permute_inds(fq_zech_sparse_vec_t vec, slong *P, const fq_zech_ctx_t ctx)
+
+ Permutes the indices of vec according to P, and resorts
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void fq_zech_sparse_vec_randtest(fq_zech_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, const fq_zech_ctx_t ctx)
+
+ Makes vec a sparse vector with nnz nonzero entries uniformly distributed
+ between 0 and len - 1, with individual entries generated by fq_zech_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_print_pretty(const fq_zech_sparse_vec_t vec, slong ioff, slong maxi, const fq_zech_ctx_t ctx)
+
+ Prints the vector of given length to ``stdout`` in a human-readable format
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void fq_zech_sparse_vec_neg(fq_zech_sparse_vec_t v, const fq_zech_sparse_vec_t u, const fq_zech_ctx_t ctx)
+
+ Sets ``v`` to the negation of ``u``
+
+.. function:: void fq_zech_sparse_vec_scalar_mul_fq_zech(fq_zech_sparse_vec_t v, const fq_zech_sparse_vec_t u, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``v`` to the scalar multiple of ``u`` by ``c``
+
+.. function:: void fq_zech_sparse_vec_add(fq_zech_sparse_vec_t w, const fq_zech_sparse_vec_t u, const fq_zech_sparse_vec_t v, const fq_zech_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``v``
+
+.. function:: void fq_zech_sparse_vec_sub(fq_zech_sparse_vec_t w, const fq_zech_sparse_vec_t u, const fq_zech_sparse_vec_t v, const fq_zech_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``v``
+
+.. function:: void fq_zech_sparse_vec_scalar_addmul_fq_zech(fq_zech_sparse_vec_t w, const fq_zech_sparse_vec_t u, const fq_zech_sparse_vec_t v, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``w`` to the sum of ``u`` and ``c` times ``v``
+
+.. function:: void fq_zech_sparse_vec_scalar_addmul_fq_zech(fq_zech_sparse_vec_t w, const fq_zech_sparse_vec_t u, const fq_zech_sparse_vec_t v, const fq_zech_t c, const fq_zech_ctx_t ctx)
+
+ Sets ``w`` to the difference of ``u`` and ``c` times ``v``
+
+.. function:: void fq_zech_sparse_vec_dot(fq_zech_t ret, const fq_zech_sparse_vec_t u, const fq_zech_sparse_vec_t v, const fq_zech_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of ``u`` and ``v``
+
+.. function:: void fq_zech_sparse_vec_dot_dense(fq_zech_t ret, const fq_zech_sparse_vec_t u, const fq_zech_struct * v, const fq_zech_ctx_t ctx)
+
+ Sets ``ret`` to the dot product of (``u``, ``v``)
diff --git a/doc/source/nmod_sparse_mat.rst b/doc/source/nmod_sparse_mat.rst
new file mode 100644
index 0000000000..52b74b7a18
--- /dev/null
+++ b/doc/source/nmod_sparse_mat.rst
@@ -0,0 +1,296 @@
+.. _nmod-sparse-mat:
+
+**nmod_sparse_mat.h** -- sparse matrices over integers mod n
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: nmod_sparse_mat_t
+
+ Holds an array of (possibly empty) sparse vectors corresponding to rows in
+ the matrix
+
+Memory management
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_init(nmod_sparse_mat_t M, slong rows, slong cols, nmod_t mod)
+
+ Initializes an empty sparse matrix ``M`` with given number of rows and columns
+
+.. function:: void nmod_sparse_mat_clear(nmod_sparse_mat_t M)
+
+ Clears the entries of the matrix ``M`` and frees the space allocated for it
+
+.. function:: void nmod_sparse_mat_swap(nmod_sparse_mat_t M1, nmod_sparse_mat_t M2)
+
+ Swaps two matrices ``M1`` and ``M2`` (no reallocaton)
+
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_zero(nmod_sparse_mat_t M)
+
+ Sets matrix ``M`` to zero (the empty sparse matrix)
+
+.. function:: void nmod_sparse_mat_one(nmod_sparse_mat_t M)
+
+ Sets matrix ``M`` to identity matrix (based on its number of rows)
+
+.. function:: void nmod_sparse_mat_set(nmod_sparse_mat_t N, const nmod_sparse_mat_t M)
+
+ Makes ``N`` a (deep) copy of ``M``
+
+.. function:: void nmod_sparse_mat_from_entries(nmod_sparse_mat_t M, slong *rows, slong *inds, mp_ptr vals, slong nnz)
+
+ Constructs matrix ``M`` from a given sequence of ``rows``, ``cols``, and
+ corresponding ``vals``, all of length ``nnz``, assumes sorted by rows
+ with no duplicate (row, col) indices
+
+.. function:: void nmod_sparse_mat_append_col(nmod_sparse_mat_t M, mp_srcptr v)
+
+ Add a dense column to the right of the matrix
+
+.. function:: void nmod_sparse_mat_append_row(nmod_sparse_mat_t M, const nmod_sparse_vec_t v)
+
+ Add a sparse row to the bottom of the matrix
+
+
+Conversion to/from dense matrix
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_from_dense(nmod_sparse_mat_t M, const nmod_mat_t dM)
+
+ Converts the dense matrix ``dM`` to a sparse matrix ``M``
+
+.. function:: void nmod_sparse_mat_to_dense(nmod_mat_t dM, const nmod_sparse_mat_t M)
+
+ Converts the sparse matrix ``M`` to a dense matrix ``dM``
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_window_init(nmod_sparse_mat_t window, const nmod_sparse_mat_t M, slong r1, slong c1, slong r2, slong c2)
+
+ Constructs a window on the sparse matrix ``M`` between rows ``r1`` and ``r2``
+ and cols ``c1`` and ``c2`` (valid as long as original matrix remains uzechified)
+
+.. function:: void nmod_sparse_mat_window_clear(nmod_sparse_mat_t window)
+
+ Clears a window
+
+.. function:: void nmod_sparse_mat_concat_horizontal(nmod_sparse_mat_t B, const nmod_sparse_mat_t M1, const nmod_sparse_mat_t M2)
+
+ Horizontally concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void nmod_sparse_mat_concat_vertical(nmod_sparse_mat_t B, const nmod_sparse_mat_t M1, const nmod_sparse_mat_t M2)
+
+ Vertically concatenates two matrices ``M1`` and ``M2`` into block matrix ``B``
+
+.. function:: void nmod_sparse_mat_split_horizontal(nmod_sparse_mat_t M1, nmod_sparse_mat_t M2, const nmod_sparse_mat_t B, slong c)
+
+ Splits ``B`` horizontally into two submatrices ``M1`` and ``M2``, dividing at column ``c``
+
+.. function:: void nmod_sparse_mat_split_vertical(nmod_sparse_mat_t M1, nmod_sparse_mat_t M2, const nmod_sparse_mat_t B, slong r)
+
+ Splits ``B`` vertically into two submatrices ``M1`` and ``M2``, dividing at row ``r``
+
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_permute_cols(nmod_sparse_mat_t M, slong *Q)
+
+ Permutes the columns indices of ``M`` according to ``Q``, and re-sorts each row
+
+.. function:: void nmod_sparse_mat_permute_rows(nmod_sparse_mat_t M, slong *P)
+
+ Permutes the rows of ``M`` according to ``P``
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void nmod_sparse_mat_randtest(nmod_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz)
+
+ Makes ``M`` a sparse matrix with between ``min_nnz`` and ``max_nnz`` nonzero
+ entries per row, with individual entries generated by nmod_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_print_pretty(const nmod_sparse_mat_t M)
+
+ Prints the matrix ``M`` to ``stdout`` in a human-readable format
+
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_is_zero(nmod_sparse_mat_t M)
+
+ Checks if the given matrix ``M`` is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void nmod_sparse_mat_equal(nmod_sparse_mat_t M1, nmod_sparse_mat_t M2, slong ioff)
+
+ Checks if ``M1`` equals ``M2``, returning `1` if so and `0` otherwise
+
+
+Transpose
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_transpose(nmod_sparse_mat_t N, nmod_sparse_mat_t M)
+
+ Transposes ``M`` into the matrix ``N`` (must have swapped rows and columns)
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_mat_neg(nmod_sparse_mat_t N, const nmod_sparse_mat_t M)
+
+ Sets ``N`` to the negation of ``M``
+
+.. function:: void nmod_sparse_mat_scalar_mul_nmod(nmod_sparse_mat_t N, const nmod_sparse_mat_t M, const nmod_t c)
+
+ Sets ``N`` to the scalar multiple of ``M`` by ``c``
+
+.. function:: void nmod_sparse_mat_add(nmod_sparse_mat_t O, const nmod_sparse_mat_t M, const nmod_sparse_mat_t N)
+
+ Sets ``O`` to the sum of ``M`` and ``N``
+
+.. function:: void nmod_sparse_mat_sub(nmod_sparse_mat_t O, const nmod_sparse_mat_t M, const nmod_sparse_mat_t N)
+
+ Sets ``O`` to the difference of ``M`` and ``N``
+
+.. function:: void nmod_sparse_mat_scalar_addmul_nmod(nmod_sparse_mat_t O, const nmod_sparse_mat_t M, const nmod_sparse_mat_t N, const nmod_t c)
+
+ Sets ``O`` to the sum of ``M`` and ``c` times ``N``
+
+.. function:: void nmod_sparse_mat_scalar_submul_nmod(nmod_sparse_mat_t O, const nmod_sparse_mat_t M, const nmod_sparse_mat_t N, const nmod_t c)
+
+ Sets ``O`` to the difference of ``M`` and ``N` times ``v``
+
+.. function:: void nmod_sparse_mat_mul_vec(mp_ptr y, const nmod_sparse_mat_t M, mp_srcptr x)
+
+ Sets ``y`` to the product of ``M`` and ``x``
+
+.. function:: void nmod_sparse_mat_mul_mat(nmod_mat_t Y, const nmod_sparse_mat_t M, const nmod_mat_t X)
+
+ Sets ``Y`` to the product of ``M`` and ``X``
+
+.. function:: slong nmod_sparse_mat_inv(nmod_sparse_mat_t N, const nmod_sparse_mat_t M)
+
+ Sets ``N`` to the "inverse" of ``M``, i.e., the matrix such that NM is
+ in reduced row-echelon form
+
+
+Decomposition/reduction
+--------------------------------------------------------------------------------
+
+.. function:: slong nmod_sparse_mat_lu(slong *P, slong *Q, nmod_sparse_mat_t L, nmod_sparse_mat_t U, const nmod_sparse_mat_t M)
+
+ Computes the decomposition PMQ = LU for a given sparse matrix ``M``, where
+ ``P`` is a row permutation, ``Q`` is a column permutation, ``L``is a lower
+ triangular matrix, and ``U`` is an upper triangular matrix, returns the rank
+
+.. function:: slong nmod_sparse_mat_rref(nmod_sparse_mat_t M)
+
+ Applies row reduction to put ``M`` in reduced row echelon form (in place)
+ and returns the rank
+
+Solving
+--------------------------------------------------------------------------------
+
+.. function:: int nmod_sparse_mat_solve_lu(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b)
+
+ Given a matrix ``M`` and target vector ``b``, use LU decomposition to find
+ a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_solve_rref(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b)
+
+ Given a matrix ``M`` and target vector ``b``, use the reduced row-echelon
+ form to find a vector ``x`` such that Mx = b, returns `1` if successful and
+ `0` if not
+
+.. function:: int nmod_sparse_mat_solve_lanczos(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b, flint_rand_t state)
+
+ Given a matrix ``M`` and target vector ``b``, use the Lanczos algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_solve_wiedemann(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b)
+
+ Given a matrix ``M`` and target vector ``b``, use the Wiedemann algorithm to
+ find a vector ``x`` such that Mx = b, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_solve_block_lanczos(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Lanczos
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_solve_block_wiedemann(mp_ptr x, const nmod_sparse_mat_t M, mp_srcptr b, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M`` and target vector ``b``, use Coppersmith's block Wiedemann
+ algorithm (with specified block size) to find a vector ``x`` such that Mx = b,
+ returns `1` if successful and `0` if not
+
+Nullvector and nullspace computation
+--------------------------------------------------------------------------------
+
+.. function:: int nmod_sparse_mat_nullvector_lanczos(mp_ptr x, const nmod_sparse_mat_t M, flint_rand_t state)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_nullvector_wiedemann(mp_ptr x, const nmod_sparse_mat_t M, flint_rand_t state)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullvector ``x``
+ s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_nullvector_block_lanczos(mp_ptr x, const nmod_sparse_mat_t M, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_nullvector_block_wiedemann(mp_ptr x, const nmod_sparse_mat_t M, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullvector ``x`` s.t. Mx = 0, returns `1` if successful and `0` if not
+
+.. function:: int nmod_sparse_mat_nullspace_rref(nmod_mat_t X, const nmod_sparse_mat_t M)
+
+ Given a matrix ``M``, use the reduced row echelon form to construct the
+ nullspace ``X`` of M (initialized by this function), returns the nullity
+
+.. function:: int nmod_sparse_mat_nullspace_lu(nmod_mat_t X, const nmod_sparse_mat_t M)
+
+ Given a matrix ``M``, use the LU decomposition to construct the nullspace ``X``
+ of M (initialized by this function), returns the nullity
+
+.. function:: int nmod_sparse_mat_nullspace_lanczos(nmod_mat_t X, const nmod_sparse_mat_t M, flint_rand_t state)
+
+ Given a matrix ``M``, use the Lanczos algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int nmod_sparse_mat_nullspace_wiedemann(nmod_mat_t X, const nmod_sparse_mat_t M, flint_rand_t state)
+
+ Given a matrix ``M``, use the Wiedemann algorithm to find a nullspace ``X``
+ of M (initialized by this function), returns the found nullity
+
+.. function:: int nmod_sparse_mat_nullspace_block_lanczos(nmod_mat_t X, const nmod_sparse_mat_t M, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M``, use Coppersmith's block Lanczos algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
+.. function:: int nmod_sparse_mat_nullspace_block_wiedemann(nmod_mat_t X, const nmod_sparse_mat_t M, slong block_size, flint_rand_t state)
+
+ Given a matrix ``M``, use Coppersmith's block Wiedemann algorithm to find a
+ nullspace ``X`` of M (initialized by this function), returns the found nullity
+
diff --git a/doc/source/nmod_sparse_vec.rst b/doc/source/nmod_sparse_vec.rst
new file mode 100644
index 0000000000..bfad8dce7b
--- /dev/null
+++ b/doc/source/nmod_sparse_vec.rst
@@ -0,0 +1,176 @@
+.. _nmod-sparse-vec:
+
+**nmod_sparse_vec.h** -- sparse vectors over integers mod word-size integers
+===============================================================================
+
+Description.
+
+Types, macros and constants
+-------------------------------------------------------------------------------
+
+.. type:: nmod_sparse_entry_t
+
+ Holds a pair (ind, val), where ind (of type slong) is an index into the
+ vector and val (of type mp_limb_t) is the value at that index
+
+.. type:: nmod_sparse_vec_t
+
+ Holds an array of nonzero entries (of type nmod_sparse_entry_struct *) of
+ specified length nnz, sorted by ind
+
+Memory management
+--------------------------------------------------------------------------------
+
+
+.. function:: void nmod_sparse_vec_init(nmod_sparse_vec_t vec)
+
+ Initializes an empty sparse vector (no allocation)
+
+.. function:: void nmod_sparse_vec_clear(nmod_sparse_vec_t vec, slong len)
+
+ Clears the entries of vec and frees the space allocated for it
+
+.. function:: void nmod_sparse_vec_swap(nmod_sparse_vec_t vec1, nmod_sparse_vec_t vec2)
+
+ Swaps two vectors (no reallocaton)
+
+Instantiation
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_zero(nmod_sparse_vec_t vec)
+
+ Sets vec to zero (the empty sparse vector)
+
+.. function:: void nmod_sparse_vec_one(nmod_sparse_vec_t vec, slong ind)
+
+ Sets vec to the ind-th basis vector (single one in position ind)
+
+.. function:: void nmod_sparse_vec_set(nmod_sparse_vec_t dst, nmod_sparse_vec_t src)
+
+ Makes dst a (deep) copy of src
+
+.. function:: void nmod_sparse_vec_set_entry(nmod_sparse_vec_t vec, slong ind, const mp_limb_t val)
+
+ Sets the value at index ``ind`` to nonzero ``val``, either replacing an existing value or
+ extending the array of entries
+
+.. function:: void nmod_sparse_vec_from_entries(nmod_sparse_vec_t vec, slong *inds, mp_ptr vals, slong nnz)
+
+ Constructs vec from a given sequence of indices and associated values, both of length nnz.
+ Assumes no duplicate indices
+
+Comparison
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_is_zero(nmod_sparse_vec_t vec)
+
+ Checks if the given vector is trivial (empty), returning `1` if so and `0`
+ otherwise
+
+.. function:: void nmod_sparse_vec_equal(const nmod_sparse_vec_t vec1, const nmod_sparse_vec_t vec2, slong ioff)
+
+ Checks if vec1 equals vec2 (with s specified column offset ioff), returning
+ `1` if so and `0` otherwise
+
+Indexing
+--------------------------------------------------------------------------------
+
+.. function:: mp_limb_t * nmod_sparse_vec_at(nmod_sparse_vec_t vec, slong ind)
+
+ Returns a pointer to the value at the index ind (or NULL if index not found)
+
+
+Conversion to/from dense vector
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_from_dense(nmod_sparse_vec_t dst, mp_srcptr src, slong len)
+
+ Converts the dense vector src of length len to a sparse vector
+
+.. function:: void nmod_sparse_vec_to_dense(mp_ptr dst, const nmod_sparse_vec_t src, slong len)
+
+ Converts the sparse vector src to a dense vector of length len
+
+
+Windows, concatenation, and splitting
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_window_init(nmod_sparse_vec_t window, const nmod_sparse_vec_t vec, slong i1, slong i2)
+
+ Constructs a window on a the sparse vector vec between indices i1 and i2
+ Note that window is only valid as long as original vector remains uzechified
+
+.. function:: void nmod_sparse_vec_window_clear(nmod_sparse_vec_t window)
+
+ Clears a window (for safety only)
+
+.. function:: void nmod_sparse_vec_concat(nmod_sparse_vec_t res, const nmod_sparse_vec_t vec1, const nmod_sparse_vec_t vec2, slong len1)
+
+ Concatenates two vectors vec1 and vec2 into res, with indices of vec2
+ offset by len1
+
+.. function:: void nmod_sparse_vec_split(nmod_sparse_vec_t res1, nmod_sparse_vec_t res1, const nmod_sparse_vec_t vec, slong ind)
+
+ Splits vec into two vectors res1 and res2, with res1 containing all entries
+ below index ind and res2 containing the rest
+
+Permutation
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_permute_inds(nmod_sparse_vec_t vec, slong *P)
+
+ Permutes the indices of vec according to P, and re-sorts
+
+
+Randomization
+--------------------------------------------------------------------------------
+
+
+.. function:: void nmod_sparse_vec_randtest(nmod_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, nmod_t mod)
+
+ Makes vec a sparse vector with nnz nonzero entries uniformly distributed
+ between 0 and len - 1, with individual entries generated by nmod_randtest
+
+
+Output
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_print_pretty(const nmod_sparse_vec_t vec, slong ioff, slong maxi, nmod_t mod)
+
+ Prints the vector of given length to ``stdout`` in a human-readable format
+
+
+Arithmetic
+--------------------------------------------------------------------------------
+
+.. function:: void nmod_sparse_vec_neg(nmod_sparse_vec_t v, const nmod_sparse_vec_t u, nmod_t mod)
+
+ Sets ``v`` to the negation of ``u``
+
+.. function:: void nmod_sparse_vec_scalar_mul_nmod(nmod_sparse_vec_t v, const nmod_sparse_vec_t u, const mp_limb_t c, nmod_t mod)
+
+ Sets ``v`` to the scalar multiple of ``u`` by ``c``
+
+.. function:: void nmod_sparse_vec_add(nmod_sparse_vec_t w, const nmod_sparse_vec_t u, const nmod_sparse_vec_t v, nmod_t mod)
+
+ Sets ``w`` to the sum of ``u`` and ``v``
+
+.. function:: void nmod_sparse_vec_sub(nmod_sparse_vec_t w, const nmod_sparse_vec_t u, const nmod_sparse_vec_t v, nmod_t mod)
+
+ Sets ``w`` to the difference of ``u`` and ``v``
+
+.. function:: void nmod_sparse_vec_scalar_addmul_nmod(nmod_sparse_vec_t w, const nmod_sparse_vec_t u, const nmod_sparse_vec_t v, const mp_limb_t c, nmod_t mod)
+
+ Sets ``w`` to the sum of ``u`` and ``c` times ``v``
+
+.. function:: void nmod_sparse_vec_scalar_submul_nmod(nmod_sparse_vec_t w, const nmod_sparse_vec_t u, const nmod_sparse_vec_t v, const mp_limb_t c, nmod_t mod)
+
+ Sets ``w`` to the difference of ``u`` and ``c` times ``v``
+
+.. function:: mp_limb_t nmod_sparse_vec_dot(const nmod_sparse_vec_t u, const nmod_sparse_vec_t v, nmod_t mod)
+
+ Sets ``ret`` to the dot product of ``u`` and ``v``
+
+.. function:: mp_limb_t nmod_sparse_vec_dot_dense(const nmod_sparse_vec_t u, mp_srcptr v, nmod_t mod)
+
+ Sets ``ret`` to the dot product of ``u`` and ``v``
diff --git a/fmpz_mat.h b/fmpz_mat.h
index faffd10523..56aa923dd3 100644
--- a/fmpz_mat.h
+++ b/fmpz_mat.h
@@ -244,6 +244,14 @@ FLINT_DLL void fmpz_mat_scalar_mod_fmpz(fmpz_mat_t B, const fmpz_mat_t A, const
/* Multiplication */
+FMPZ_MAT_INLINE
+void fmpz_mat_mul_vec(fmpz *y, const fmpz_mat_t A, fmpz *x)
+{
+ slong i;
+ for (i = 0; i < A->r; ++i)
+ _fmpz_vec_dot(&y[i], A->rows[i], x, A->c);
+}
+
FLINT_DLL void fmpz_mat_mul(fmpz_mat_t C, const fmpz_mat_t A, const fmpz_mat_t B);
FLINT_DLL void fmpz_mat_mul_classical(fmpz_mat_t C, const fmpz_mat_t A,
diff --git a/fmpz_mpoly/divides_array.c b/fmpz_mpoly/divides_array.c
index 0b233afe73..d218f76f06 100644
--- a/fmpz_mpoly/divides_array.c
+++ b/fmpz_mpoly/divides_array.c
@@ -14,7 +14,6 @@
#include "flint.h"
#include "fmpz.h"
#include "fmpz_mpoly.h"
-#include "hashmap.h"
/* improve locality */
#define BLOCK 128
diff --git a/fmpz_sparse_mat.h b/fmpz_sparse_mat.h
new file mode 100644
index 0000000000..965d324bb2
--- /dev/null
+++ b/fmpz_sparse_mat.h
@@ -0,0 +1,687 @@
+/*
+ Copyright (C) 2010 William Hart
+ Copyright (C) 2010,2011 Fredrik Johansson
+ Copyright (C) 2014 Ashish Kedia
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+#ifndef FMPZ_SPARSE_MAT_H
+#define FMPZ_SPARSE_MAT_H
+
+#ifdef FMPZ_SPARSE_MAT_INLINES_C
+#define FMPZ_SPARSE_MAT_INLINE FLINT_DLL
+#else
+#define FMPZ_SPARSE_MAT_INLINE static __inline__
+#endif
+
+#undef ulong
+#define ulong ulongxx /* interferes with system includes */
+#include
+#undef ulong
+
+#include
+#define ulong mp_limb_t
+#include "flint.h"
+#include "fmpz.h"
+#include "fmpz_vec.h"
+#include "hashmap.h"
+#include "heap.h"
+#include "nmod_sparse_mat.h"
+#include "fmpz_sparse_vec.h"
+#include "fmpz_mat.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* A sparse matrix is a list of sparse row vectors */
+typedef struct
+{
+ fmpz_sparse_vec_struct *rows;
+ slong r;
+ slong c;
+ slong c_off;
+}
+fmpz_sparse_mat_struct;
+
+typedef fmpz_sparse_mat_struct fmpz_sparse_mat_t[1];
+
+#define LT(M, r) M->rows[r].entries[0]
+
+/* Can package matrix with its column support, to enable various operations */
+typedef struct
+{
+ fmpz_sparse_mat_struct *M;
+ hashmap_struct *cols;
+}
+fmpz_sparse_mat_with_transpose_struct;
+
+typedef fmpz_sparse_mat_with_transpose_struct fmpz_sparse_mat_with_transpose_t[1];
+
+/* Memory management */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_init (fmpz_sparse_mat_t M, slong rows, slong cols)
+{
+ FLINT_ASSERT(rows >= 0 && cols >= 0);
+ M->rows = flint_calloc(rows, sizeof(*M->rows));
+ M->r = rows;
+ M->c = cols;
+ M->c_off = 0;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_clear (fmpz_sparse_mat_t M)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i)
+ {
+ fmpz_sparse_vec_clear(&M->rows[i]);
+ }
+ flint_free(M->rows);
+ memset(M, 0, sizeof(*M));
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_resize (fmpz_sparse_mat_t M, slong rows, slong cols)
+{
+ slong i;
+ FLINT_ASSERT(rows >= 0 && cols >= 0);
+ if (M->r != rows) {
+ if (M->r > rows)
+ {
+ for (i = rows; i < M->r; ++i)
+ {
+ fmpz_sparse_vec_clear(&M->rows[i]);
+ }
+ }
+ M->rows = flint_realloc(M->rows, rows*sizeof(*M->rows));
+ if (M->r < rows)
+ {
+ memset(M->rows+M->r, 0, (rows-M->r)*sizeof(*M->rows));
+ }
+ M->r = rows;
+ }
+ if (cols < M->c)
+ {
+ for (i = 0; i < M->r; ++i)
+ {
+ fmpz_sparse_vec_resize(&M->rows[i], cols);
+ }
+ }
+ M->c = cols;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_swap (fmpz_sparse_mat_t M1, fmpz_sparse_mat_t M2)
+{
+ fmpz_sparse_mat_t tmp;
+ *tmp = *M1; *M1 = *M2; *M2 = *tmp;
+}
+
+FLINT_DLL
+slong fmpz_sparse_mat_max_bits(const fmpz_sparse_mat_t v);
+
+/* One-time instantiation */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_zero (fmpz_sparse_mat_t M)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) fmpz_sparse_vec_zero(&M->rows[i]);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_one (fmpz_sparse_mat_t M)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) fmpz_sparse_vec_one(&M->rows[i], i);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_set (fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M)
+{
+ slong i, rmax = FLINT_MIN(M->r, M->r);
+ if (M==N) return;
+ for (i = 0; i < rmax; ++i) fmpz_sparse_vec_set(&N->rows[i], &M->rows[i], M->c_off);
+}
+
+FLINT_DLL
+void fmpz_sparse_mat_from_entries(fmpz_sparse_mat_t M, slong * rows, slong * cols, fmpz * vals, slong nnz);
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_append_col (fmpz_sparse_mat_t M, const fmpz *v)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) fmpz_sparse_vec_set_entry(&M->rows[i], M->c, &v[i]);
+ M->c += 1;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_append_row (fmpz_sparse_mat_t M, const fmpz_sparse_vec_t v)
+{
+ M->rows = realloc(M->rows, (M->r+1)*sizeof(*M->rows));
+ memset(M->rows + M->r, 0, sizeof(*M->rows));
+ fmpz_sparse_vec_set(&M->rows[M->r], v, 0);
+ M->r += 1;
+}
+
+/* Convert from/to dense matrix */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_from_dense (fmpz_sparse_mat_t M, const fmpz_mat_t dM)
+{
+ slong i, rmax = FLINT_MIN(M->r, dM->r);
+ if (M->c == 0 || dM->c == 0) {fmpz_sparse_mat_zero(M); return;}
+ for (i = 0; i < rmax; ++i) fmpz_sparse_vec_from_dense(&M->rows[i], dM->rows[i], dM->c);
+}
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_to_dense (fmpz_mat_t dM, const fmpz_sparse_mat_t M)
+{
+ slong i, rmax = FLINT_MIN(M->r, dM->r);
+ if (M->c == 0 || dM->c == 0) {fmpz_mat_zero(dM); return;}
+ for (i = 0; i < rmax; ++i)
+ fmpz_sparse_vec_to_dense(dM->rows[i], &M->rows[i], dM->c);
+}
+
+/* Convert to/from modular matrix */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_get_nmod_sparse_mat(nmod_sparse_mat_t Amod, const fmpz_sparse_mat_t A)
+{
+ slong i;
+ for (i = 0; i < A->r; i++)
+ fmpz_sparse_vec_get_nmod_sparse_vec(&Amod->rows[i], &A->rows[i], Amod->mod);
+}
+
+void fmpz_sparse_mat_multi_mod_ui_precomp(nmod_sparse_mat_struct * residues, slong nres, const fmpz_sparse_mat_t M,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp);
+
+void fmpz_sparse_mat_multi_mod_ui(nmod_sparse_mat_struct * residues, slong nres, const fmpz_sparse_mat_t M);
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_set_nmod_sparse_mat_unsigned(fmpz_sparse_mat_t A, const nmod_sparse_mat_t Amod)
+{
+ slong i;
+ for (i = 0; i < A->r; i++)
+ fmpz_sparse_vec_set_nmod_sparse_vec_unsigned(&A->rows[i], &Amod->rows[i]);
+}
+
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_set_nmod_sparse_mat(fmpz_sparse_mat_t A, const nmod_sparse_mat_t Amod)
+{
+ slong i;
+ for (i = 0; i < A->r; i++)
+ fmpz_sparse_vec_set_nmod_sparse_vec(&A->rows[i], &Amod->rows[i], Amod->mod);
+}
+
+FLINT_DLL
+void fmpz_sparse_mat_CRT_ui(fmpz_sparse_mat_t res, const fmpz_sparse_mat_t mat1,
+ const fmpz_t m1, const nmod_sparse_mat_t mat2, int sign);
+
+
+FLINT_DLL
+void fmpz_sparse_mat_multi_CRT_ui_precomp(fmpz_sparse_mat_t M, nmod_sparse_mat_struct * residues, slong nres,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp, int sign);
+
+FLINT_DLL
+void fmpz_sparse_mat_multi_CRT_ui(fmpz_sparse_mat_t mat, nmod_sparse_mat_struct * residues, slong nres, int sign);
+
+/* Windows, concatenation, and splitting */
+FLINT_DLL
+void fmpz_sparse_mat_window_init (fmpz_sparse_mat_t W, const fmpz_sparse_mat_t M, slong r1, slong c1, slong r2, slong c2);
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_window_clear (fmpz_sparse_mat_t W)
+{
+ flint_free(W->rows);
+ memset(W, 0, sizeof(*W));
+}
+
+
+/* Combine M1 and M2 into block matrix B = [M1 M2] */
+/* B->r must equal M1->r and M2->r */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_concat_horizontal(fmpz_sparse_mat_t B,
+ const fmpz_sparse_mat_t M1, const fmpz_sparse_mat_t M2)
+{
+ slong i;
+ B->c = M1->c + M2->c;
+ for (i = 0; i < B->r; ++i)
+ fmpz_sparse_vec_concat(&B->rows[i], &M1->rows[i], &M2->rows[i], M1->c);
+}
+/* Combine M1 and M2 into block matrix B = [M1^t M1^t]^t */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_concat_vertical (fmpz_sparse_mat_t B, const fmpz_sparse_mat_t M1, const fmpz_sparse_mat_t M2)
+{
+ slong i;
+ B->c = FLINT_MAX(M1->c, M2->c);
+ for (i = 0; i < M1->r; ++i)
+ fmpz_sparse_vec_set(&B->rows[i], &M1->rows[i], M1->c_off);
+ for (i = M1->r; i < B->r; ++i)
+ fmpz_sparse_vec_set(&B->rows[i], &M2->rows[i-M1->r], M2->c_off);
+}
+
+/* Split block matrix B = [M1 M2] into submatrices M1 and M2 */
+/* M1->r and M2->r must equal B->r */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_split_horizontal (fmpz_sparse_mat_t M1, fmpz_sparse_mat_t M2, const fmpz_sparse_mat_t B, slong c)
+{
+ slong i;
+ for (i = 0; i < B->r; ++i) fmpz_sparse_vec_split(&M1->rows[i], &M2->rows[i], &B->rows[i], c);
+}
+
+/* Split block matix B = [M1^t M1^t]^t into submatrices M1 and M2 */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_split_vertical (fmpz_sparse_mat_t M1, fmpz_sparse_mat_t M2, const fmpz_sparse_mat_t B, slong r)
+{
+ slong i;
+ r = FLINT_MIN(r, B->r);
+ for (i = 0; i < r; ++i) fmpz_sparse_vec_set(&M1->rows[i], &B->rows[i], B->c_off);
+ for (i = r; i < B->r; ++i) fmpz_sparse_vec_set(&M2->rows[i-r], &B->rows[i], B->c_off);
+}
+
+/* Matrix permutation */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_permute_cols(fmpz_sparse_mat_t M, slong *Q)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i)
+ {
+ if (!M->rows[i].nnz) continue;
+ fmpz_sparse_vec_permute_inds(&M->rows[i], Q);
+ qsort(M->rows[i].entries, M->rows[i].nnz, sizeof(*M->rows[i].entries), fmpz_sparse_entry_cmp);
+ }
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_permute_rows(fmpz_sparse_mat_t M, slong *P)
+{
+ slong i;
+ fmpz_sparse_vec_struct *prows;
+ prows = flint_calloc(M->r, sizeof(*prows));
+ for (i = 0; i < M->r; ++i) prows[P[i]] = M->rows[i];
+ memcpy(M->rows, prows, M->r*sizeof(*M->rows));
+ flint_free(prows);
+}
+
+/* Random matrix generation */
+FLINT_DLL void fmpz_sparse_mat_randtest (fmpz_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, flint_bitcnt_t bits);
+FLINT_DLL void fmpz_sparse_mat_randtest_unsigned (fmpz_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, flint_bitcnt_t bits);
+/*
+FLINT_DLL void fmpz_sparse_mat_randfull (fmpz_sparse_mat_t M, flint_rand_t state, fmpz_ctx_t ctx);
+FLINT_DLL int fmpz_sparse_mat_randpermdiag(fmpz_sparse_mat_t M, flint_rand_t state,
+ const fmpz *diag, slong n);
+FLINT_DLL void fmpz_sparse_mat_randrank (fmpz_sparse_mat_t, flint_rand_t state, slong rank, fmpz_ctx_t ctx);
+FLINT_DLL void fmpz_sparse_mat_randops (fmpz_sparse_mat_t M, slong count, flint_rand_t state, fmpz_ctx_t ctx);
+FLINT_DLL void fmpz_sparse_mat_randtril (fmpz_sparse_mat_t M, flint_rand_t state, int unit, fmpz_ctx_t ctx);
+FLINT_DLL void fmpz_sparse_mat_randtriu (fmpz_sparse_mat_t M, flint_rand_t state, int unit, fmpz_ctx_t ctx);
+ */
+
+FLINT_DLL void fmpz_sparse_mat_print_pretty (const fmpz_sparse_mat_t M);
+
+FMPZ_SPARSE_MAT_INLINE
+int fmpz_sparse_mat_equal (const fmpz_sparse_mat_t M1, const fmpz_sparse_mat_t M2)
+{
+ slong i;
+ if (M1->r != M2->r) return 0;
+ for (i = 0; i < M1->r; ++i)
+ if (!fmpz_sparse_vec_equal(&M1->rows[i], &M2->rows[i], M1->c_off-M2->c_off)) return 0;
+ return 1;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int fmpz_sparse_mat_is_zero (const fmpz_sparse_mat_t M)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i)
+ if (!fmpz_sparse_vec_is_zero(&M->rows[i])) return 0;
+ return 1;
+}
+
+/* Must have M->r == N->c and M->c == N->r */
+FLINT_DLL
+void fmpz_sparse_mat_transpose (fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M);
+
+/* Arithmetic */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_neg (fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_neg(&N->rows[i], &M->rows[i]);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_mul_fmpz(fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M, const fmpz_t c)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_scalar_mul_fmpz(&N->rows[i], &M->rows[i], c);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_mul_diag_fmpz(fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M, fmpz * D)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_scalar_mul_fmpz(&N->rows[i], &M->rows[i], &D[i]);
+}
+
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_divexact_fmpz(fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M, const fmpz_t c)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_scalar_divexact_fmpz(&N->rows[i], &M->rows[i], c);
+}
+
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_mod_fmpz(fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M, const fmpz_t mod)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_scalar_mod_fmpz(&N->rows[i], &M->rows[i], mod);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_mods_fmpz(fmpz_sparse_mat_t N, const fmpz_sparse_mat_t M, const fmpz_t mod)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) fmpz_sparse_vec_scalar_mods_fmpz(&N->rows[i], &M->rows[i], mod);
+}
+
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_add (fmpz_sparse_mat_t O, const fmpz_sparse_mat_t M, const fmpz_sparse_mat_t N)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) fmpz_sparse_vec_add(&O->rows[i], &M->rows[i], &N->rows[i]);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_sub (fmpz_sparse_mat_t O, const fmpz_sparse_mat_t M, const fmpz_sparse_mat_t N)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) fmpz_sparse_vec_sub(&O->rows[i], &M->rows[i], &N->rows[i]);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_addmul_fmpz(fmpz_sparse_mat_t O, const fmpz_sparse_mat_t M, const fmpz_sparse_mat_t N, const fmpz_t c)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) fmpz_sparse_vec_scalar_addmul_fmpz(&O->rows[i], &M->rows[i], &N->rows[i], c);
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_scalar_submul_fmpz(fmpz_sparse_mat_t O, const fmpz_sparse_mat_t M, const fmpz_sparse_mat_t N, const fmpz_t c)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) fmpz_sparse_vec_scalar_submul_fmpz(&O->rows[i], &M->rows[i], &N->rows[i], c);
+}
+
+/* Matrix-vector and matrix-matrix multipliciation */
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_mul_vec (fmpz *y, const fmpz_sparse_mat_t M, const fmpz *x)
+{
+ slong i;
+ if (M->c == 0) _fmpz_vec_zero(y, M->r);
+ else for (i = 0; i < M->r; ++i) fmpz_sparse_vec_dot_dense(&y[i], &M->rows[i], x);
+}
+FMPZ_SPARSE_MAT_INLINE
+void fmpz_sparse_mat_mul_mat (fmpz_mat_t Y, const fmpz_sparse_mat_t M, const fmpz_mat_t X)
+{
+ slong i, j;
+ FLINT_ASSERT(M->r == Y->r && M->c == X->r && X->c == Y->c);
+ fmpz_mat_zero (Y);
+ for (i = 0; i < M->r; ++i)
+ {
+ for (j = 0; j < M->rows[i].nnz; ++j)
+ {
+ fmpz_sparse_entry_struct *e = &M->rows[i].entries[j];
+ _fmpz_vec_scalar_addmul_fmpz(Y->rows[i], X->rows[e->ind], X->c, e->val);
+ }
+ }
+}
+
+/* Memory management for matrix with transpose */
+FLINT_DLL
+void _fmpz_sparse_mat_with_transpose_init(fmpz_sparse_mat_with_transpose_t MT, fmpz_sparse_mat_t M);
+
+FMPZ_SPARSE_MAT_INLINE
+void _fmpz_sparse_mat_with_transpose_clear(fmpz_sparse_mat_with_transpose_t MT)
+{
+ slong c;
+ for (c = 0; c < MT->M->c; ++c)
+ hashmap_clear(&MT->cols[c]);
+ flint_free(MT->cols);
+ memset(MT, 0, sizeof(*MT));
+}
+
+FMPZ_SPARSE_MAT_INLINE
+void _fmpz_sparse_mat_with_transpose_print_pretty(fmpz_sparse_mat_with_transpose_t MT)
+{
+ slong i, j;
+ fmpz_sparse_mat_print_pretty(MT->M);
+ flint_printf("Transpose: \nSupport: ");
+ for (i = 0; i < MT->M->c; ++i)
+ {
+ flint_printf("%wd ", MT->cols[i].num);
+ }
+ flint_printf("\n");
+ for (i = 0; i < MT->M->c; ++i)
+ {
+ flint_printf("%wd: [", i); fflush(stdout);
+ for (j = 0; j < MT->cols[i].num; ++j)
+ {
+ if (j > 0) flint_printf(" ");
+ flint_printf("%wd: ", MT->cols[i].keys[j]); fflush(stdout);
+ fmpz_print(*((fmpz_t *) (MT->cols[i].vals[j])));
+ }
+ flint_printf("]\n");
+ }
+}
+
+FLINT_DLL
+void _fmpz_sparse_mat_with_transpose_fix_support(fmpz_sparse_mat_with_transpose_t MT, slong r, slong *osupp, slong onnz);
+
+#define MT_FIX(MT, r, ...) \
+{\
+ slong *supp, nnz; \
+ nnz = _fmpz_sparse_vec_support(&supp, &MT->M->rows[r]); \
+ __VA_ARGS__; \
+ _fmpz_sparse_mat_with_transpose_fix_support(MT, r, supp, nnz);\
+ flint_free(supp);\
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_col(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r, slong col)
+{
+ MT_FIX(MT, r, fmpz_sparse_vec_gauss_elim_col(&MT->M->rows[r], &MT->M->rows[pr], col));
+ return fmpz_sparse_vec_at(&MT->M->rows[r], col) == NULL;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r)
+{
+ MT_FIX(MT, r, fmpz_sparse_vec_gauss_elim(&MT->M->rows[r], &MT->M->rows[pr]));
+ return fmpz_sparse_vec_at(&MT->M->rows[r], MT->M->rows[pr].entries[0].ind) == NULL;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_mod(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r, const fmpz_t mod)
+{
+ MT_FIX(MT, r,
+ fmpz_sparse_vec_gauss_elim(&MT->M->rows[r], &MT->M->rows[pr]);
+ fmpz_sparse_vec_scalar_mod_fmpz(&MT->M->rows[r], &MT->M->rows[r], mod));
+ return fmpz_sparse_vec_at(&MT->M->rows[r], MT->M->rows[pr].entries[0].ind) == NULL;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_mods(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r, const fmpz_t mod)
+{
+ MT_FIX(MT, r,
+ fmpz_sparse_vec_gauss_elim(&MT->M->rows[r], &MT->M->rows[pr]);
+ fmpz_sparse_vec_scalar_mods_fmpz(&MT->M->rows[r], &MT->M->rows[r], mod));
+ return fmpz_sparse_vec_at(&MT->M->rows[r], MT->M->rows[pr].entries[0].ind) == NULL;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_ext(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r)
+{
+ /* If leading entries do not match, or leading entry of pr divides that of r, just a normal elimination */
+ fmpz_sparse_entry_struct *pe = &MT->M->rows[pr].entries[0], *e = &MT->M->rows[r].entries[0];
+ if (pe->ind != e->ind)
+ return _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, r);
+
+ MT_FIX(MT, pr, MT_FIX(MT, r,
+ fmpz_sparse_vec_gauss_elim_ext(&MT->M->rows[r], &MT->M->rows[pr])
+ ));
+ return 1;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_ext_mod(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r, const fmpz_t mod)
+{
+ /* If leading entries do not match, or leading entry of pr divides that of r, just a normal elimination */
+ fmpz_sparse_entry_struct *pe = &MT->M->rows[pr].entries[0], *e = &MT->M->rows[r].entries[0];
+ if (pe->ind != e->ind)
+ return _fmpz_sparse_mat_with_transpose_gauss_elim_mod(MT, pr, r, mod);
+
+ MT_FIX(MT, pr, MT_FIX(MT, r,
+ fmpz_sparse_vec_gauss_elim_ext(&MT->M->rows[r], &MT->M->rows[pr]);
+ fmpz_sparse_vec_scalar_mod_fmpz(&MT->M->rows[pr], &MT->M->rows[pr], mod);
+ fmpz_sparse_vec_scalar_mod_fmpz(&MT->M->rows[r], &MT->M->rows[r], mod);
+ ));
+ return 1;
+}
+
+FMPZ_SPARSE_MAT_INLINE
+int _fmpz_sparse_mat_with_transpose_gauss_elim_ext_mods(fmpz_sparse_mat_with_transpose_t MT, slong pr, slong r, const fmpz_t mod)
+{
+ /* If leading entries do not match, or leading entry of pr divides that of r, just a normal elimination */
+ fmpz_sparse_entry_struct *pe = &MT->M->rows[pr].entries[0], *e = &MT->M->rows[r].entries[0];
+ if (pe->ind != e->ind)
+ return _fmpz_sparse_mat_with_transpose_gauss_elim_mods(MT, pr, r, mod);
+
+ MT_FIX(MT, pr, MT_FIX(MT, r,
+ fmpz_sparse_vec_gauss_elim_ext(&MT->M->rows[r], &MT->M->rows[pr]);
+ fmpz_sparse_vec_scalar_mods_fmpz(&MT->M->rows[pr], &MT->M->rows[pr], mod);
+ fmpz_sparse_vec_scalar_mods_fmpz(&MT->M->rows[r], &MT->M->rows[r], mod);
+ ));
+ return 1;
+}
+
+/* Utility computations */
+FLINT_DLL
+void fmpz_sparse_mat_content(fmpz_t mat_gcd, const fmpz_sparse_mat_t M);
+
+FLINT_DLL
+void fmpz_sparse_mat_gram(fmpz_mat_t B, const fmpz_sparse_mat_t A);
+
+/* Solving */
+FLINT_DLL
+void fmpz_sparse_mat_solve_bound(fmpz_t N, fmpz_t D, const fmpz_sparse_mat_t A, const fmpz_mat_t B);
+
+FLINT_DLL
+int fmpz_sparse_mat_solve_dixon(fmpz_mat_t X, fmpz_t mod, const fmpz_sparse_mat_t A, const fmpz_mat_t B);
+
+FMPZ_SPARSE_MAT_INLINE
+int fmpz_sparse_mat_solve_vec_dixon(fmpz * x, fmpz_t mod, const fmpz_sparse_mat_t A, fmpz *b)
+{
+ int ret;
+ slong i;
+ fmpz_mat_t X, B;
+ fmpz_mat_init(X, A->c, 1);
+ fmpz_mat_init(B, A->r, 1);
+ for (i = 0; i < A->r; ++i) fmpz_set(fmpz_mat_entry(B, i, 0), &b[i]);
+ ret = fmpz_sparse_mat_solve_dixon(X, mod, A, B);
+ for (i = 0; i < A->c; ++i) fmpz_set(&x[i], fmpz_mat_entry(X, i, 0));
+ fmpz_mat_clear(X);
+ fmpz_mat_clear(B);
+ return ret;
+}
+
+FLINT_DLL
+int fmpz_sparse_mat_solve_dixon_den(fmpz_mat_t X, fmpz_t den, const fmpz_sparse_mat_t A, const fmpz_mat_t B);
+
+FMPZ_SPARSE_MAT_INLINE
+int fmpz_sparse_mat_solve_vec_dixon_den(fmpz * x, fmpz_t den, const fmpz_sparse_mat_t A, fmpz *b)
+{
+ int ret;
+ slong i;
+ fmpz_mat_t X, B;
+ fmpz_mat_init(X, A->c, 1);
+ fmpz_mat_init(B, A->r, 1);
+ for (i = 0; i < A->r; ++i) fmpz_set(fmpz_mat_entry(B, i, 0), &b[i]);
+ ret = fmpz_sparse_mat_solve_dixon_den(X, den, A, B);
+ for (i = 0; i < A->c; ++i) fmpz_set(&x[i], fmpz_mat_entry(X, i, 0));
+ fmpz_mat_clear(X);
+ fmpz_mat_clear(B);
+ return ret;
+}
+
+/* Determinant computation */
+FLINT_DLL
+void fmpz_sparse_mat_det_bound(fmpz_t bound, const fmpz_sparse_mat_t A);
+FLINT_DLL
+void fmpz_sparse_mat_det_cofactor(fmpz_t det, const fmpz_sparse_mat_t M);
+FLINT_DLL
+void fmpz_sparse_mat_det_bareiss(fmpz_t det, const fmpz_sparse_mat_t M);
+FLINT_DLL
+void fmpz_sparse_mat_det_divisor(fmpz_t d, const fmpz_sparse_mat_t M);
+FLINT_DLL
+void fmpz_sparse_mat_det_modular_accelerated(fmpz_t det, const fmpz_sparse_mat_t A, int proved);
+FLINT_DLL
+void fmpz_sparse_mat_det_modular_given_divisor(fmpz_t det, const fmpz_sparse_mat_t A, const fmpz_t d, int proved);
+FLINT_DLL
+void fmpz_sparse_mat_det_modular(fmpz_t det, const fmpz_sparse_mat_t A, int proved);
+FLINT_DLL
+void fmpz_sparse_mat_det(fmpz_t det, const fmpz_sparse_mat_t A);
+
+/* Fraction-free LU factorization */
+FLINT_DLL
+slong fmpz_sparse_mat_fflu(fmpz *D, slong *P, slong *Q, fmpz_sparse_mat_t L, fmpz_sparse_mat_t U,
+ const fmpz_sparse_mat_t M);
+
+/* Hermite normal form */
+FLINT_DLL
+int fmpz_sparse_mat_is_in_hnf(const fmpz_sparse_mat_t A);
+
+FLINT_DLL
+slong fmpz_sparse_mat_hnf_classical(fmpz_sparse_mat_t M);
+
+FLINT_DLL
+slong fmpz_sparse_mat_hnf_xgcd(fmpz_sparse_mat_t M);
+
+FLINT_DLL
+slong fmpz_sparse_mat_hnf_minors(fmpz_sparse_mat_t M);
+
+FLINT_DLL
+slong fmpz_sparse_mat_hnf_modular(fmpz_sparse_mat_t M, const fmpz_t det);
+
+FLINT_DLL
+slong fmpz_sparse_mat_hnf_modular_eldiv(fmpz_sparse_mat_t M, const fmpz_t n);
+
+/* Modular forms */
+slong fmpz_sparse_mat_howell_form_mod(fmpz_sparse_mat_t M, const fmpz_t mod);
+
+FLINT_DLL
+slong fmpz_sparse_mat_strong_echelon_form_mod(fmpz_sparse_mat_t M, const fmpz_t mod);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/fmpz_sparse_mat/CRT_ui.c b/fmpz_sparse_mat/CRT_ui.c
new file mode 100644
index 0000000000..577b456dff
--- /dev/null
+++ b/fmpz_sparse_mat/CRT_ui.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "nmod_sparse_mat.h"
+#include "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_CRT_ui(fmpz_sparse_mat_t res, const fmpz_sparse_mat_t mat1,
+ const fmpz_t m1, const nmod_sparse_mat_t mat2, int sign)
+{
+ slong i;
+ mp_limb_t m1i_m2 = n_invmod(fmpz_fdiv_ui(m1, mat2->mod.n), mat2->mod.n);
+
+ if (m1i_m2 == 0)
+ {
+ flint_printf("Exception (fmpz_mat_CRT_ui). m1 not invertible modulo m2.\n");
+ flint_abort();
+ }
+ for (i = 0; i < mat1->r; i++)
+ fmpz_sparse_vec_CRT_ui(&res->rows[i], &mat1->rows[i], m1, &mat2->rows[i], mat2->mod, m1i_m2, sign);
+}
+
diff --git a/fmpz_sparse_mat/content.c b/fmpz_sparse_mat/content.c
new file mode 100644
index 0000000000..f9a7d44b34
--- /dev/null
+++ b/fmpz_sparse_mat/content.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2015 Dharak Kharod
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "fmpz.h"
+
+void fmpz_sparse_mat_content(fmpz_t mat_gcd, const fmpz_sparse_mat_t M)
+{
+ slong i, j;
+ fmpz_set_si(mat_gcd,0);
+ for (i = 0; i < M->r; i++ )
+ {
+ for (j = 0; j < M->rows[i].nnz; j++)
+ {
+ fmpz_gcd(mat_gcd, mat_gcd, M->rows[i].entries[j].val);
+ if (fmpz_is_one(mat_gcd)) return;
+ }
+ }
+}
+
diff --git a/fmpz_sparse_mat/det.c b/fmpz_sparse_mat/det.c
new file mode 100644
index 0000000000..1bcad6cab3
--- /dev/null
+++ b/fmpz_sparse_mat/det.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2010,2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_det(fmpz_t det, const fmpz_sparse_mat_t A)
+{
+ slong dim = A->r;
+ FLINT_ASSERT(A->r == A->c);
+
+ if (dim < 5)
+ fmpz_sparse_mat_det_cofactor(det, A);
+ else if (dim < 25)
+ fmpz_sparse_mat_det_bareiss(det, A);
+ else if (dim < 60)
+ fmpz_sparse_mat_det_modular(det, A, 1);
+ else
+ {
+ slong bits = fmpz_sparse_mat_max_bits(A);
+
+ if (dim < FLINT_ABS(bits))
+ fmpz_sparse_mat_det_modular(det, A, 1);
+ else
+ fmpz_sparse_mat_det_modular_accelerated(det, A, 1);
+ }
+}
diff --git a/fmpz_sparse_mat/det_bareiss.c b/fmpz_sparse_mat/det_bareiss.c
new file mode 100644
index 0000000000..3d9ea39291
--- /dev/null
+++ b/fmpz_sparse_mat/det_bareiss.c
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2010,2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "perm.h"
+
+void
+fmpz_sparse_mat_det_bareiss(fmpz_t det, const fmpz_sparse_mat_t M)
+{
+ slong i, rk, *P, *Q;
+ fmpz *D;
+ fmpz_sparse_mat_t L, U;
+ FLINT_ASSERT(M->r == M->c);
+ fmpz_one(det);
+ if (M->r == UWORD(0)) return;
+
+ P = flint_malloc(M->r*sizeof(*P));
+ Q = flint_malloc(M->c*sizeof(*P));
+ D = _fmpz_vec_init(M->r);
+ fmpz_sparse_mat_init(L, M->r, M->c);
+ fmpz_sparse_mat_init(U, M->r, M->c);
+ rk = fmpz_sparse_mat_fflu(D, P, Q, L, U, M);
+ if (rk != M->r) fmpz_zero(det);
+ else
+ {
+ for (i = 0; i < M->r; ++i)
+ fmpz_mul(det, det, U->rows[i].entries[0].val);
+ for (i = 0; i < M->r; ++i)
+ fmpz_divexact(det, det, &D[i]);
+ if (_perm_parity(P, M->r) ^ _perm_parity(Q, M->c))
+ fmpz_neg(det, det);
+ }
+
+ flint_free(P);
+ flint_free(Q);
+ _fmpz_vec_clear(D, M->r);
+ fmpz_sparse_mat_clear(L);
+ fmpz_sparse_mat_clear(U);
+}
diff --git a/fmpz_sparse_mat/det_bound.c b/fmpz_sparse_mat/det_bound.c
new file mode 100644
index 0000000000..b14f1ce389
--- /dev/null
+++ b/fmpz_sparse_mat/det_bound.c
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_det_bound(fmpz_t bound, const fmpz_sparse_mat_t A)
+{
+ fmpz_t s, t;
+ slong i, j;
+ FLINT_ASSERT(A->r == A->c);
+
+ fmpz_init(s);
+ fmpz_init(t);
+
+ /* bound = II_i ceil(||A[i]||_2) */
+ fmpz_one(bound);
+ for (i = 0; i < A->r; i++)
+ {
+ fmpz_zero(s);
+ for (j = 0; j < A->rows[i].nnz; j++)
+ fmpz_addmul(s, A->rows[i].entries[j].val, A->rows[i].entries[j].val);
+ fmpz_sqrtrem(s, t, s);
+
+ if (!fmpz_is_zero(t))
+ fmpz_add_ui(s, s, UWORD(1));
+
+ fmpz_mul(bound, bound, s);
+ }
+
+ fmpz_clear(s);
+ fmpz_clear(t);
+}
diff --git a/fmpz_sparse_mat/det_cofactor.c b/fmpz_sparse_mat/det_cofactor.c
new file mode 100644
index 0000000000..eccb5bbe3d
--- /dev/null
+++ b/fmpz_sparse_mat/det_cofactor.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010,2011,2018 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_det_cofactor(fmpz_t det, const fmpz_sparse_mat_t M)
+{
+ fmpz_mat_t dM;
+ FLINT_ASSERT(M->r == M->c);
+ if (M->r != M->c) {fmpz_zero(det); return;}
+ if (M->r > 4)
+ {
+ flint_printf("Exception (fmpz_sparse_mat_det_cofactor). dim > 4 not implemented.");
+ flint_abort();
+ }
+ fmpz_mat_init(dM, M->r, M->c);
+ fmpz_sparse_mat_to_dense(dM, M);
+ fmpz_mat_det_cofactor(det, dM);
+ fmpz_mat_clear(dM);
+}
diff --git a/fmpz_sparse_mat/det_divisor.c b/fmpz_sparse_mat/det_divisor.c
new file mode 100644
index 0000000000..bb8d8a64b9
--- /dev/null
+++ b/fmpz_sparse_mat/det_divisor.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "fmpq.h"
+
+void
+fmpz_sparse_mat_det_divisor(fmpz_t d, const fmpz_sparse_mat_t M)
+{
+ int success;
+ slong i;
+ fmpz_mat_t X, B;
+ fmpz_t t, u, v, mod;
+ FLINT_ASSERT(M->r == M->c);
+
+ fmpz_mat_init(X, M->c, 1);
+ fmpz_mat_init(B, M->r, 1);
+ fmpz_init(t);
+ fmpz_init(u);
+ fmpz_init(v);
+ fmpz_init(mod);
+
+ /* Create a "random" vector */
+ for (i = 0; i < M->r; i++)
+ fmpz_set_si(fmpz_mat_entry(B, i, 0), 2*(i % 2) - 1);
+
+ success = fmpz_sparse_mat_solve_dixon(X, mod, M, B);
+ if (success)
+ {
+ fmpz_one(d);
+ for (i = 0; i < M->r; i++)
+ {
+ fmpz_mul(t, d, fmpz_mat_entry(X, i, 0));
+ fmpz_fdiv_qr(u, t, t, mod);
+ if (!_fmpq_reconstruct_fmpz(u, v, t, mod))
+ {
+ flint_printf("Exception (fmpz_mat_det_divisor): "
+ "Rational reconstruction failed.\n");
+ flint_abort();
+ }
+
+ fmpz_mul(d, v, d);
+ }
+ }
+ else fmpz_zero(d);
+
+ fmpz_mat_clear(X);
+ fmpz_mat_clear(B);
+ fmpz_clear(t);
+ fmpz_clear(u);
+ fmpz_clear(v);
+ fmpz_clear(mod);
+}
diff --git a/fmpz_sparse_mat/det_modular.c b/fmpz_sparse_mat/det_modular.c
new file mode 100644
index 0000000000..a56b1e1e5a
--- /dev/null
+++ b/fmpz_sparse_mat/det_modular.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_det_modular(fmpz_t det, const fmpz_sparse_mat_t A, int proved)
+{
+ fmpz_t d;
+ FLINT_ASSERT(A->r == A->c);
+ fmpz_init(d);
+ fmpz_one(d);
+ fmpz_sparse_mat_det_modular_given_divisor(det, A, d, proved);
+ fmpz_clear(d);
+}
diff --git a/fmpz_sparse_mat/det_modular_accelerated.c b/fmpz_sparse_mat/det_modular_accelerated.c
new file mode 100644
index 0000000000..ad39bd9b33
--- /dev/null
+++ b/fmpz_sparse_mat/det_modular_accelerated.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_det_modular_accelerated(fmpz_t det, const fmpz_sparse_mat_t A, int proved)
+{
+ fmpz_t d;
+ FLINT_ASSERT(A->r == A->c);
+ fmpz_init(d);
+ fmpz_sparse_mat_det_divisor(d, A);
+ fmpz_sparse_mat_det_modular_given_divisor(det, A, d, proved);
+ fmpz_clear(d);
+}
diff --git a/fmpz_sparse_mat/det_modular_given_divisor.c b/fmpz_sparse_mat/det_modular_given_divisor.c
new file mode 100644
index 0000000000..bbb678f93e
--- /dev/null
+++ b/fmpz_sparse_mat/det_modular_given_divisor.c
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "nmod_sparse_mat.h"
+#include "fmpz_sparse_mat.h"
+
+/* Enable to exercise corner cases */
+#define DEBUG_USE_SMALL_PRIMES 0
+
+
+static mp_limb_t
+next_good_prime(const fmpz_t d, mp_limb_t p)
+{
+ mp_limb_t r = 0;
+
+ while (r == 0)
+ {
+ p = n_nextprime(p, 0);
+ r = fmpz_fdiv_ui(d, p);
+ }
+
+ return p;
+}
+
+
+void
+fmpz_sparse_mat_det_modular_given_divisor(fmpz_t det, const fmpz_sparse_mat_t A,
+ const fmpz_t d, int proved)
+{
+ fmpz_t bound, prod, stable_prod, x, xnew;
+ mp_limb_t p, xmod;
+ nmod_t mod;
+ nmod_sparse_mat_t Amod;
+ slong n = A->r;
+ FLINT_ASSERT(A->r == A->c);
+
+ if (A->r == 0) {fmpz_one(det); return;}
+ if (fmpz_is_zero(d)) {fmpz_zero(det); return;}
+
+ fmpz_init(bound);
+ fmpz_init(prod);
+ fmpz_init(stable_prod);
+ fmpz_init(x);
+ fmpz_init(xnew);
+
+ /* Bound x = det(A) / d */
+ fmpz_sparse_mat_det_bound(bound, A);
+ fmpz_mul_ui(bound, bound, UWORD(2)); /* accomodate sign */
+ fmpz_cdiv_q(bound, bound, d);
+
+ nmod_init(&mod, 2);
+ nmod_sparse_mat_init(Amod, n, n, mod);
+ fmpz_zero(x);
+ fmpz_one(prod);
+
+ p = UWORD(1) << NMOD_MAT_OPTIMAL_MODULUS_BITS;
+
+ /* Compute x = det(A) / d */
+ while (fmpz_cmp(prod, bound) <= 0)
+ {
+ p = next_good_prime(d, p);
+ nmod_init(&mod, p);
+ Amod->mod = mod;
+ fmpz_sparse_mat_get_nmod_sparse_mat(Amod, A);
+
+ /* Compute x = det(A) / d mod p */
+ xmod = nmod_sparse_mat_det(Amod);
+ xmod = n_mulmod2_preinv(xmod,
+ n_invmod(fmpz_fdiv_ui(d, p), p), Amod->mod.n, Amod->mod.ninv);
+
+ fmpz_CRT_ui(xnew, x, prod, xmod, p, 1);
+
+ if (fmpz_equal(xnew, x))
+ {
+ fmpz_mul_ui(stable_prod, stable_prod, p);
+ if (!proved && fmpz_bits(stable_prod) > 100) break;
+ }
+ else fmpz_set_ui(stable_prod, p);
+
+ fmpz_mul_ui(prod, prod, p);
+ fmpz_set(x, xnew);
+ }
+
+ /* det(A) = x * d */
+ fmpz_mul(det, x, d);
+
+ nmod_sparse_mat_clear(Amod);
+ fmpz_clear(bound);
+ fmpz_clear(prod);
+ fmpz_clear(stable_prod);
+ fmpz_clear(x);
+ fmpz_clear(xnew);
+}
diff --git a/fmpz_sparse_mat/fflu.c b/fmpz_sparse_mat/fflu.c
new file mode 100644
index 0000000000..5038505bb6
--- /dev/null
+++ b/fmpz_sparse_mat/fflu.c
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "hashmap.h"
+#include "heap.h"
+#include "perm.h"
+#include "longlong.h"
+
+static void update_heap(heap_t h, const fmpz_sparse_mat_with_transpose_t MT, slong r, slong *osupp, slong onnz)
+{
+ slong i = 0, j = 0, oc, nc, c;
+ fmpz_sparse_vec_struct *row = &MT->M->rows[r];
+ while (1)
+ {
+ oc = (i==onnz) ? MT->M->c : osupp[i];
+ nc = (j==row->nnz) ? MT->M->c : row->entries[j].ind;
+ c = FLINT_MIN(oc, nc);
+ if (c >= MT->M->c) break;
+ if (oc != nc) heap_adjust(h, c, MT->cols[c].num);
+ if (c==oc) ++i;
+ if (c==nc) ++j;
+ }
+}
+
+slong
+fmpz_sparse_mat_fflu(fmpz *D, slong *P, slong *Q, fmpz_sparse_mat_t L, fmpz_sparse_mat_t U,
+ const fmpz_sparse_mat_t M)
+{
+ slong i, j, r, c, pr, pc, rank, remr, remc, *supp, nnz;
+ heap_t h;
+ fmpz_t pivot, one, tmp;
+ fmpz_sparse_vec_struct *prow, *row;
+ hashmap_struct *hcol;
+ fmpz_t *hval;
+ fmpz_sparse_mat_with_transpose_t UT;
+
+ for (i = 0; i < M->r; ++i) fmpz_one(&D[i]);
+ if (M->r == 0 || M->c == 0)
+ {
+ fmpz_sparse_mat_zero (L);
+ fmpz_sparse_mat_zero (U);
+ for (i = 0; i < M->r; ++i) P[i] = i;
+ for (i = 0; i < M->c; ++i) Q[i] = i;
+ return 0;
+ }
+ fmpz_init(pivot);
+ fmpz_init(tmp);
+ fmpz_init_set_ui(one, UWORD(1));
+ fmpz_sparse_mat_zero(L);
+ fmpz_sparse_mat_set (U, M);
+ _fmpz_sparse_mat_with_transpose_init(UT, U);
+
+ /* Initialize permutations */
+ remr = M->r, remc = M->c;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!U->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+ for (c = 0; c < M->c; ++c)
+ {
+ if (!UT->cols[c].num) Q[c] = --remc;
+ else Q[c] = -1;
+ }
+
+ /* Make heap of nonzero columns by size */
+ heap_init(h, M->c);
+ for (c = 0; c < M->c; ++c)
+ heap_push(h, UT->cols[c].num);
+
+ /* Run elimination */
+ rank = 0;
+
+ while (h->num > 0)
+ {
+ /* Get lowest weight column (top of heap) */
+ pc = heap_pop(h, NULL);
+ hcol = &UT->cols[pc];
+
+ /* Get lowest weight incident row */
+ prow = NULL, pr = -1;
+ for (i = 0; i < hcol->num; ++i)
+ {
+ r = hcol->keys[i];
+ row = &U->rows[r];
+ if (prow == NULL || row->nnz < prow->nnz)
+ pr = r, prow = row;
+ }
+ if (pr == -1) {if (Q[pc] == -1) Q[pc] = --remc; continue;}
+ P[pr] = Q[pc] = rank++; /* Move pivot row and col to front */
+
+ /* Get pivot */
+ fmpz_set(pivot, *fmpz_sparse_vec_at(prow, pc));
+ fmpz_sparse_vec_set_entry(&L->rows[pr], pc, one);
+
+ /* Remove pivot row from transpose */
+ for (j = 0; j < prow->nnz; ++j)
+ hashmap_rem(&UT->cols[prow->entries[j].ind], pr);
+
+ /* Use pivot row to fraction-free Gaussian eliminate other incident rows */
+ while (hcol->num > 0)
+ {
+ r = hcol->keys[0];
+ nnz = _fmpz_sparse_vec_support(&supp, &U->rows[r]);
+ fmpz_mul(&D[r], &D[r], pivot);
+ hval = hcol->vals[0], fmpz_set(tmp, *hval);
+ fmpz_sparse_vec_scalar_mul_fmpz(&U->rows[r], &U->rows[r], pivot);
+ fmpz_sparse_vec_scalar_mul_fmpz(&L->rows[r], &L->rows[r], pivot);
+ fmpz_sparse_vec_set_entry(&L->rows[r], pc, tmp);
+ _fmpz_sparse_mat_with_transpose_gauss_elim_col(UT, pr, r, pc);
+ update_heap(h, UT, r, supp, nnz);
+ if (U->rows[r].nnz == 0) P[r] = --remr;
+ }
+ }
+ heap_clear(h);
+ _fmpz_sparse_mat_with_transpose_clear(UT);
+
+ /* Reorder rows and cols in L and U */
+ fmpz_sparse_mat_permute_cols (L, Q);
+ fmpz_sparse_mat_permute_rows (L, P);
+ fmpz_sparse_mat_permute_cols (U, Q);
+ fmpz_sparse_mat_permute_rows (U, P);
+ fmpz_clear(pivot);
+ fmpz_clear(tmp);
+ return rank;
+}
diff --git a/fmpz_sparse_mat/from_entries.c b/fmpz_sparse_mat/from_entries.c
new file mode 100644
index 0000000000..d31194a885
--- /dev/null
+++ b/fmpz_sparse_mat/from_entries.c
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void fmpz_sparse_mat_from_entries(fmpz_sparse_mat_t M, slong * rows, slong * cols, fmpz * vals, slong nnz)
+{
+ slong r, i, j;
+ for (r = i = 0; r < M->r; ++r, i = j)
+ {
+ for (j = i; j < nnz && rows[j]==r; ++j);
+ fmpz_sparse_vec_from_entries(&M->rows[r], cols+i, vals+i, j-i);
+ }
+}
diff --git a/fmpz_sparse_mat/gram.c b/fmpz_sparse_mat/gram.c
new file mode 100644
index 0000000000..2cbb39531a
--- /dev/null
+++ b/fmpz_sparse_mat/gram.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2014 Abhinav Baid
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void fmpz_sparse_mat_gram(fmpz_mat_t B, const fmpz_sparse_mat_t M)
+{
+ slong i, j;
+
+ if (M->r == 0) fmpz_mat_zero(B);
+ else
+ for (i = 0; i < M->r; i++)
+ for (j = 0; j < M->r; j++)
+ fmpz_sparse_vec_dot(&B->rows[i][j], &M->rows[i], &M->rows[j]);
+}
diff --git a/fmpz_sparse_mat/hnf_classical.c b/fmpz_sparse_mat/hnf_classical.c
new file mode 100644
index 0000000000..d8acb15b07
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_classical.c
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "hashmap.h"
+#include "fmpz_sparse_mat.h"
+
+slong
+fmpz_sparse_mat_hnf_classical(fmpz_sparse_mat_t M)
+{
+ slong i, r, pr, pc, rank, remr, next_pr, nnz;
+ slong *P; /* Row permutation */
+ slong *irows; /* Set of row incident on a given column */
+ slong nnp, next_nnp; /* Number of such rows which are not previous pivots */
+ slong npp; /* Number of such rows which are previous pivots */
+ fmpz_sparse_mat_with_transpose_t MT;
+ hashmap_struct *hcol;
+
+ if (M->r == 0 || M->c == 0) return 0;
+
+ /* Construct virtual transpose */
+ _fmpz_sparse_mat_with_transpose_init(MT, M);
+
+ /* Set up permutation */
+ P = flint_malloc(M->r*sizeof(*P));
+ remr = M->r;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+ irows = flint_malloc(M->r * sizeof(*irows));
+
+ for (rank = pc = 0; pc < M->c; ++pc)
+ {
+ hcol = &MT->cols[pc]; nnz = hcol->num;
+ if (!nnz) continue;
+ pr = next_pr = -1, next_nnp = nnz, npp = 0;
+
+ do
+ {
+ pr = next_pr, next_pr = -1;
+ nnp = next_nnp, next_nnp = 0;
+ for (i = 0; i < nnp; ++i)
+ {
+ r = (pr == -1) ? hcol->keys[i] : irows[i];
+ if (pr >= 0) /* Reduce row r by row pr */
+ {
+ if (_fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, r))
+ {
+ if (M->rows[r].nnz == 0) P[r] = --remr;
+ continue;
+ }
+ }
+ else if (P[r] >= 0) /* Record incident previous pivot rows at end of irows */
+ {
+ irows[hcol->num - (++npp)] = r;
+ continue;
+ }
+
+ /* Either add r (back) to irows or make it the next pivot row */
+
+ if (next_pr >= 0 && fmpz_cmpabs(LT(M, r).val, LT(M, next_pr).val) >= 0)
+ {
+ irows[next_nnp++] = r;
+ continue;
+ }
+ if (next_pr >= 0) irows[next_nnp++] = next_pr;
+ next_pr = r;
+ }
+ if (pr != -1) irows[next_nnp++] = pr;
+ } while (next_pr != -1); /* Stop when no more reduction to be done */
+ if (pr == -1) continue; /* No incident rows which are not previous pivots */
+
+ if (fmpz_sgn(LT(M, pr).val) < 0)
+ fmpz_sparse_vec_neg(&M->rows[pr], &M->rows[pr]);
+
+ /* Now use row pr to reduce the previous pivot rows */
+ for (i = nnz - npp; i < nnz; ++i)
+ _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, irows[i]);
+ P[pr] = rank++;
+ }
+
+ /* Apply row permutation */
+ fmpz_sparse_mat_permute_rows (M, P);
+
+ flint_free(P);
+ flint_free(irows);
+ _fmpz_sparse_mat_with_transpose_clear(MT);
+ return rank;
+}
diff --git a/fmpz_sparse_mat/hnf_minors.c b/fmpz_sparse_mat/hnf_minors.c
new file mode 100644
index 0000000000..cec18c70a8
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_minors.c
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2017 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+/*
+ This is the algorithm of Kannan, Bachem, "Polynomial algorithms for computing
+ the Smith and Hermite normal forms of an integer matrix", Siam J. Comput.,
+ Vol. 8, No. 4, pp. 499-507.
+*/
+slong
+fmpz_sparse_mat_hnf_minors(fmpz_sparse_mat_t M)
+{
+
+ slong i, r, c, pr, pc, rank, remr, nnz;
+ slong *P; /* Row permutation */
+ slong *Pr; /* Assignment of pivot row to each column */
+ fmpz_sparse_mat_with_transpose_t MT;
+ hashmap_struct *hcol; /* Virtual L^t and one of its rows */
+ fmpz_t g, a, b, one;
+
+ if (M->r == 0 || M->c == 0) return 0;
+ fmpz_init(g);
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_init_set_ui(one, UWORD(1));
+
+ /* Construct virtual transpose */
+ _fmpz_sparse_mat_with_transpose_init(MT, M);
+
+ /* Set up permutation and its "inverse" */
+ P = flint_malloc(M->r*sizeof(*P));
+ Pr = flint_malloc(M->c*sizeof(*Pr));
+ remr = M->r;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+
+ for (rank = pc = 0; pc < M->c; ++pc)
+ {
+ Pr[pc] = -1;
+
+ /* Find first non-empty row which is not a previous pivot and eliminate cols up to pc */
+ for (r = 0; r < M->r; ++r)
+ {
+ if (P[r] >= 0) continue;
+
+ /* Reduce r by previous pivot rows */
+ while ((c = M->rows[r].entries[0].ind) < pc)
+ {
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext(MT, Pr[c], r);
+ if (M->rows[r].nnz == 0) {P[r] = --remr; break;}
+ }
+ if (M->rows[r].nnz > 0 && M->rows[r].entries[0].ind == pc) break;
+ }
+ if (r == M->r) continue; /* No viable pivot for column */
+ pr = r;
+
+ if (fmpz_sgn(LT(M, r).val) < 0)
+ fmpz_sparse_vec_neg(&M->rows[r], &M->rows[r]);
+
+ /* Use this row to reduce previous pivot rows */
+ hcol = &MT->cols[pc]; nnz = hcol->num;
+ for (i = 0; i < nnz; ++i)
+ {
+ r = hcol->keys[i];
+ if (r != pr && P[r] >= 0)
+ _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, r);
+ }
+ Pr[pc] = pr;
+ P[pr] = rank++;
+ }
+
+ /* Deal with any remaining rows */
+ for (r = 0; r < M->r; ++r)
+ {
+ if (P[r] >= 0) continue;
+
+ /* Reduce r by previous pivot rows */
+ while ((c = M->rows[r].entries[0].ind) < M->c)
+ {
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext(MT, Pr[c], r);
+ if (M->rows[r].nnz == 0) {P[r] = --remr; break;}
+ }
+ }
+
+ /* Since pivot rows were modified, need to re-reduce previous pivot rows */
+ for (pc = 0; pc < M->c; ++pc)
+ {
+ if (Pr[pc] == -1) continue; /* No pivot for this column */
+ pr = Pr[pc];
+ hcol = &MT->cols[pc]; nnz = hcol->num;
+ for (i = 0; i < nnz; ++i)
+ {
+ r = hcol->keys[i];
+ if (r == pr) continue;
+
+ /* All other incident rows must be previous pivots */
+ _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, r);
+ }
+ }
+
+ /* Apply row permutation */
+ fmpz_sparse_mat_permute_rows (M, P);
+
+ flint_free(P);
+ flint_free(Pr);
+ _fmpz_sparse_mat_with_transpose_clear(MT);
+ fmpz_clear(g);
+ fmpz_clear(a);
+ fmpz_clear(b);
+ fmpz_clear(one);
+ return rank;
+}
diff --git a/fmpz_sparse_mat/hnf_modular.c b/fmpz_sparse_mat/hnf_modular.c
new file mode 100644
index 0000000000..c35acce450
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_modular.c
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+slong
+fmpz_sparse_mat_hnf_modular(fmpz_sparse_mat_t M, const fmpz_t det)
+{
+ slong i, r, pr, pc, rank, remr, nnz;
+ slong *P; /* Row permutation */
+ slong *irows; /* Set of rows incident on a given column */
+ slong nnp; /* Number of such rows which are not previous pivots */
+ slong npp; /* Number of such rows which are previous pivots */
+ fmpz_sparse_mat_with_transpose_t MT;
+ hashmap_struct *hcol; /* Virtual L^t and one of its rows */
+ fmpz_t g, a, b, one, rem_det;
+
+ if (M->r == 0 || M->c == 0 || M->r != M->c || fmpz_is_zero(det)) return 0;
+ fmpz_init(g);
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_init_set_ui(one, UWORD(1));
+ fmpz_init_set(rem_det, det);
+
+ /* Construct virtual transpose */
+ _fmpz_sparse_mat_with_transpose_init(MT, M);
+
+ /* Set up permutation */
+ P = flint_malloc(M->r*sizeof(*P));
+ remr = M->r;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+
+ irows = NULL;
+ for (rank = pc = 0; pc < M->c; ++pc)
+ {
+ hcol = &MT->cols[pc]; nnz = hcol->num;
+ if (!nnz) continue;
+ irows = flint_realloc(irows, nnz*sizeof(*irows));
+ pr = -1, nnp = 0, npp = 0;
+
+ /* Find incident row pr which is not a previous pivot and has minimal leading term */
+ /* Make pi_0 ... pi_{nnp-1} the other incident rows which are not previous pivots */
+ /* and pi_{nnz-npp} ... pi_{nnz - 1} the incident rows which are previous pivots */
+ for (i = 0; i < nnz; ++i)
+ {
+ r = hcol->keys[i];
+ if (P[r] >= 0)
+ irows[hcol->num - (++npp)] = r;
+ else if (pr >= 0 && fmpz_cmpabs(LT(M, r).val, LT(M, pr).val) >= 0)
+ irows[nnp++] = r;
+ else
+ {
+ if (pr >= 0) irows[nnp++] = pr;
+ pr = r;
+ }
+ }
+ if (pr == -1)
+ {
+ /* Set pivot col in some non-pivot row to rem_det */
+ for (pr = 0; pr < M->r; ++pr)
+ if (P[pr] == -1) break;
+ fmpz_sparse_vec_set_entry(&M->rows[pr], pc, rem_det);
+ fmpz_one(rem_det);
+ }
+ else
+ {
+ /* Eliminate non-pivot rows */
+ for (i = 0; i < nnp; ++i)
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext_mods(MT, pr, irows[i], rem_det);
+
+ /* Minimize row modulo rem_det */
+ fmpz_xgcd(g, a, b, LT(M, pr).val, rem_det);
+
+ MT_FIX(MT, pr,
+ fmpz_sparse_vec_scalar_mul_fmpz(&M->rows[pr], &M->rows[pr], a);
+ fmpz_sparse_vec_scalar_mods_fmpz(&M->rows[pr], &M->rows[pr], rem_det);
+ if (M->rows[pr].nnz == 0 || LT(M, pr).ind != pc)
+ fmpz_sparse_vec_set_entry(&M->rows[pr], pc, rem_det);
+ );
+
+ fmpz_divexact(rem_det, rem_det, g);
+ }
+ /* Reduce previous pivot rows */
+ for (i = nnz - npp; i < nnz; ++i)
+ _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, irows[i]);
+ P[pr] = rank++;
+ }
+ /* Apply row permutation */
+ fmpz_sparse_mat_permute_rows (M, P);
+
+ flint_free(P);
+ flint_free(irows);
+ fmpz_clear(a);
+ fmpz_clear(b);
+ fmpz_clear(one);
+ fmpz_clear(rem_det);
+ _fmpz_sparse_mat_with_transpose_clear(MT);
+ return M->r;
+}
diff --git a/fmpz_sparse_mat/hnf_modular_eldiv.c b/fmpz_sparse_mat/hnf_modular_eldiv.c
new file mode 100644
index 0000000000..2a5e334672
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_modular_eldiv.c
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2015 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "nmod_sparse_mat.h"
+
+slong
+fmpz_sparse_mat_hnf_modular_eldiv(fmpz_sparse_mat_t M, const fmpz_t n)
+{
+ slong i;
+
+ if (fmpz_sparse_mat_is_zero(M)) return 0;
+
+ if (fmpz_abs_fits_ui(n))
+ {
+ /* TODO: have nmod version */
+/* nmod_init(&mod, fmpz_get_ui(n));
+ nmod_sparse_mat_init(Mmod, M->r, M->c, mod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(Mmod, M);
+ rank = nmod_sparse_mat_strong_echelon_form(Mmod);
+ fmpz_sparse_mat_set_nmod_sparse_mat_unsigned(M, Mmod);
+ nmod_sparse_mat_clear(Mmod);
+ */ }
+
+ fmpz_sparse_mat_strong_echelon_form_mod(M, n);
+
+ for (i = 0; i < M->r; ++i)
+ if (fmpz_sparse_vec_is_zero(&M->rows[i]))
+ fmpz_sparse_vec_set_entry(&M->rows[i], i, n);
+ return M->r;
+}
+
diff --git a/fmpz_sparse_mat/hnf_pernet_stein.c b/fmpz_sparse_mat/hnf_pernet_stein.c
new file mode 100644
index 0000000000..836052ac81
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_pernet_stein.c
@@ -0,0 +1,638 @@
+/*
+ Copyright (C) 2014, 2015 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_mat.h"
+#include "fmpq_mat.h"
+#include "perm.h"
+
+#if 0
+
+static void
+add_columns(fmpz_mat_t H, const fmpz_mat_t B, const fmpz_mat_t H1, flint_rand_t state)
+{
+ int neg;
+ slong i, j, n, bits;
+ fmpz_t den, tmp, one;
+ fmpq_t num, alpha;
+ fmpz_mat_t Bu, B1, cols, k;
+ fmpq_mat_t H1_q, cols_q, x;
+
+ n = B->r;
+
+ fmpz_mat_init(Bu, n, n);
+ fmpz_mat_init(B1, n - 1, n);
+ fmpz_mat_init(cols, n, B->c - n);
+ fmpz_mat_init(k, n, 1);
+ fmpq_mat_init(x, n, B->c - n);
+ fmpq_mat_init(cols_q, n, B->c - n);
+ fmpq_mat_init(H1_q, n, n);
+
+ for (i = 0; i < n; i++)
+ for (j = 0; j < cols->c; j++)
+ fmpz_set(fmpz_mat_entry(cols, i, j), fmpz_mat_entry(B, i, n + j));
+ for (i = 0; i < n - 1; i++)
+ {
+ for (j = 0; j < n; j++)
+ {
+ fmpz_set(fmpz_mat_entry(Bu, i, j), fmpz_mat_entry(B, i, j));
+ fmpz_set(fmpz_mat_entry(B1, i, j), fmpz_mat_entry(B, i, j));
+ }
+ }
+
+ /* find kernel basis vector */
+ if (fmpz_mat_nullspace(k, B1) != 1)
+ {
+ flint_printf("Exception (fmpz_mat_hnf_pernet_stein). "
+ "Nullspace was not dimension one.\n");
+ flint_abort();
+ }
+
+ bits = fmpz_mat_max_bits(B1);
+ if (bits < 0)
+ bits = -bits;
+
+ fmpz_mat_clear(B1);
+
+ fmpz_init(tmp);
+
+ /* set the last row of Bu to be random, such that Bu is nonsingular */
+ while (fmpz_is_zero(tmp))
+ {
+ _fmpz_vec_randtest(Bu->rows[n - 1], state, n, bits);
+
+ fmpz_zero(tmp);
+ for (j = 0; j < n; j++)
+ fmpz_addmul(tmp, fmpz_mat_entry(Bu, n - 1, j),
+ fmpz_mat_entry(k, j, 0));
+ }
+ fmpz_clear(tmp);
+
+ /* solve Bu*x = cols */
+ if (!fmpq_mat_solve_fmpz_mat(x, Bu, cols))
+ {
+ flint_printf("Exception (fmpz_mat_hnf_pernet_stein). "
+ "Singular input matrix for solve.");
+ flint_abort();
+ }
+
+ /* fix final row */
+ fmpq_init(num);
+ fmpz_init(den);
+ fmpq_init(alpha);
+ fmpz_init(one);
+ fmpz_one(one);
+
+ /* compute denominator */
+ for (i = 0; i < n; i++)
+ fmpz_addmul(den, fmpz_mat_entry(B, n - 1, i), fmpz_mat_entry(k, i, 0));
+ neg = (fmpz_sgn(den) < 0);
+ if (neg)
+ fmpz_neg(den, den);
+
+ for (j = 0; j < B->c - H1->c; j++)
+ {
+ fmpq_zero(num);
+ for (i = 0; i < n; i++)
+ {
+ _fmpq_addmul(fmpq_numref(num), fmpq_denref(num),
+ fmpz_mat_entry(B, n - 1, i), one,
+ fmpq_mat_entry_num(x, i, j),
+ fmpq_mat_entry_den(x, i, j));
+ }
+ _fmpq_sub(fmpq_numref(alpha), fmpq_denref(alpha),
+ fmpz_mat_entry(B, n - 1, n + j), one,
+ fmpq_numref(num), fmpq_denref(num));
+
+ _fmpq_mul(fmpq_numref(alpha), fmpq_denref(alpha),
+ fmpq_numref(alpha), fmpq_denref(alpha), one, den);
+ if (neg)
+ fmpq_neg(alpha, alpha);
+
+ /* x_i += alpha*k */
+ for (i = 0; i < n; i++)
+ {
+ _fmpq_addmul(fmpq_mat_entry_num(x, i, j),
+ fmpq_mat_entry_den(x, i, j), fmpq_numref(alpha),
+ fmpq_denref(alpha), fmpz_mat_entry(k, i, 0), one);
+ }
+ }
+
+ fmpq_clear(num);
+ fmpz_clear(den);
+ fmpz_clear(one);
+ fmpq_clear(alpha);
+
+ /* set cols = H1*x and place in position in H */
+ fmpq_mat_set_fmpz_mat(H1_q, H1);
+ fmpq_mat_mul(cols_q, H1_q, x);
+ fmpq_mat_get_fmpz_mat(cols, cols_q);
+ for (i = 0; i < n; i++)
+ {
+ for (j = 0; j < n; j++)
+ fmpz_set(fmpz_mat_entry(H, i, j), fmpz_mat_entry(H1, i, j));
+ for (j = n; j < H->c; j++)
+ fmpz_set(fmpz_mat_entry(H, i, j), fmpz_mat_entry(cols, i, j - n));
+ }
+
+ fmpq_mat_clear(H1_q);
+ fmpq_mat_clear(x);
+ fmpq_mat_clear(cols_q);
+ fmpz_mat_clear(k);
+ fmpz_mat_clear(cols);
+ fmpz_mat_clear(Bu);
+}
+
+/* takes input matrix H with rows 0 to start_row - 1 in HNF to a HNF matrix */
+static void
+add_rows(fmpz_mat_t H, slong start_row, slong *pivots, slong num_pivots)
+{
+ slong i, i2, j, j2, new_row, row;
+ fmpz_t b, d, u, v, r1d, r2d, q;
+
+ fmpz_init(b);
+ fmpz_init(d);
+ fmpz_init(u);
+ fmpz_init(v);
+ fmpz_init(r1d);
+ fmpz_init(r2d);
+ fmpz_init(q);
+
+ for (row = start_row; row < H->r; row++)
+ {
+ /* reduce row to be added with existing */
+ for (i = j = 0; i < num_pivots; i++)
+ {
+ /* check if added row can still be reduced */
+ for (; j < pivots[i]; j++)
+ if (!fmpz_is_zero(fmpz_mat_entry(H, row, j)))
+ break;
+ if (j < pivots[i])
+ break;
+ if (fmpz_is_zero(fmpz_mat_entry(H, row, j)))
+ continue;
+ fmpz_xgcd(d, u, v, fmpz_mat_entry(H, i, j),
+ fmpz_mat_entry(H, row, j));
+ fmpz_divexact(r1d, fmpz_mat_entry(H, i, j), d);
+ fmpz_divexact(r2d, fmpz_mat_entry(H, row, j), d);
+ for (j2 = j; j2 < H->c; j2++)
+ {
+ fmpz_mul(b, u, fmpz_mat_entry(H, i, j2));
+ fmpz_addmul(b, v, fmpz_mat_entry(H, row, j2));
+ fmpz_mul(fmpz_mat_entry(H, row, j2), r1d,
+ fmpz_mat_entry(H, row, j2));
+ fmpz_submul(fmpz_mat_entry(H, row, j2), r2d,
+ fmpz_mat_entry(H, i, j2));
+ fmpz_set(fmpz_mat_entry(H, i, j2), b);
+ }
+ }
+
+ /* find first non-zero entry of the added row */
+ for (j = 0; j < H->c && fmpz_is_zero(fmpz_mat_entry(H, row, j)); j++) ;
+ new_row = row;
+ if (j != H->c) /* last row non-zero, move to correct position */
+ {
+ if (fmpz_sgn(fmpz_mat_entry(H, row, j)) < 0)
+ {
+ for (j2 = j; j2 < H->c; j2++)
+ {
+ fmpz_neg(fmpz_mat_entry(H, row, j2),
+ fmpz_mat_entry(H, row, j2));
+ }
+ }
+ do
+ {
+ if (new_row < row)
+ fmpz_mat_swap_rows(H, NULL, new_row, new_row + 1);
+ if (new_row == 0)
+ break;
+ new_row--;
+ for (j2 = 0; j2 < H->c &&
+ fmpz_is_zero(fmpz_mat_entry(H, new_row, j2)); j2++) ;
+ }
+ while (j2 > j);
+ }
+
+ /* recompute pivots */
+ for (i = new_row, j = 0; i <= row && i < H->c; i++, j++)
+ {
+ for (; j < H->c && fmpz_is_zero(fmpz_mat_entry(H, i, j)); j++) ;
+ if (j == H->c)
+ break;
+ pivots[i] = j;
+ num_pivots = i + 1;
+ }
+
+ /* reduce above pivot entries */
+ for (i = 0; i < num_pivots; i++)
+ {
+ for (i2 = 0; i2 < i; i2++)
+ {
+ fmpz_fdiv_q(q, fmpz_mat_entry(H, i2, pivots[i]),
+ fmpz_mat_entry(H, i, pivots[i]));
+ for (j2 = pivots[i]; j2 < H->c; j2++)
+ {
+ fmpz_submul(fmpz_mat_entry(H, i2, j2), q,
+ fmpz_mat_entry(H, i, j2));
+ }
+ }
+ }
+ }
+
+ fmpz_clear(q);
+ fmpz_clear(r2d);
+ fmpz_clear(r1d);
+ fmpz_clear(v);
+ fmpz_clear(u);
+ fmpz_clear(d);
+ fmpz_clear(b);
+}
+
+static void
+double_det(fmpz_t d1, fmpz_t d2, const fmpz_mat_t B, const fmpz_mat_t c,
+ const fmpz_mat_t d)
+{
+ slong i, j, n;
+ slong *P;
+ mp_limb_t p, u1mod, u2mod, v1mod, v2mod;
+ fmpz_t bound, prod, s1, s2, t, u1, u2, v1, v2;
+ fmpz_mat_t dt, Bt;
+ fmpq_t tmpq;
+ fmpq_mat_t x;
+ nmod_mat_t Btmod;
+
+ n = B->c;
+
+ fmpz_mat_init(dt, n, 1);
+ fmpz_mat_init(Bt, n, n);
+ fmpq_mat_init(x, n, 1);
+
+ for (i = 0; i < n; i++)
+ {
+ for (j = 0; j < n - 1; j++)
+ fmpz_set(fmpz_mat_entry(Bt, i, j), fmpz_mat_entry(B, j, i));
+ fmpz_set(fmpz_mat_entry(Bt, i, n - 1), fmpz_mat_entry(c, 0, i));
+ }
+
+ /* solve B^Tx = d^T */
+ fmpz_mat_transpose(dt, d);
+ fmpq_mat_solve_fmpz_mat(x, Bt, dt);
+
+ if (!fmpq_is_zero(fmpq_mat_entry(x, n - 1, 0)))
+ {
+ fmpz_init(bound);
+ fmpz_init(prod);
+ fmpz_init(t);
+ fmpz_init(s1);
+ fmpz_init(s2);
+ fmpz_init(u1);
+ fmpz_init(u2);
+ fmpz_init(v1);
+ fmpz_init(v2);
+
+ /* compute lcm of denominators of vectors x and y */
+ fmpq_init(tmpq);
+ fmpz_one(u1);
+ fmpz_one(u2);
+ for (i = 0; i < n - 1; i++)
+ {
+ fmpz_lcm(u1, u1, fmpq_mat_entry_den(x, i, 0));
+ fmpq_div(tmpq, fmpq_mat_entry(x, i, 0),
+ fmpq_mat_entry(x, n - 1, 0));
+ fmpz_lcm(u2, u2, fmpq_denref(tmpq));
+ }
+ fmpz_lcm(u1, u1, fmpq_mat_entry_den(x, n - 1, 0));
+ fmpq_inv(tmpq, fmpq_mat_entry(x, n - 1, 0));
+ fmpz_lcm(u2, u2, fmpq_denref(tmpq));
+ fmpq_clear(tmpq);
+
+ /* compute Hadamard bounds */
+ fmpz_one(bound);
+ for (j = 0; j < n - 1; j++)
+ {
+ fmpz_zero(s1);
+ for (i = 0; i < n; i++)
+ fmpz_addmul(s1, fmpz_mat_entry(Bt, i, j),
+ fmpz_mat_entry(Bt, i, j));
+ fmpz_sqrtrem(s1, t, s1);
+ if (!fmpz_is_zero(t))
+ fmpz_add_ui(s1, s1, UWORD(1));
+ fmpz_mul(bound, bound, s1);
+ }
+ fmpz_zero(s1);
+ fmpz_zero(s2);
+ for (j = 0; j < n; j++)
+ {
+ fmpz_addmul(s1, fmpz_mat_entry(c, 0, j), fmpz_mat_entry(c, 0, j));
+ fmpz_addmul(s2, fmpz_mat_entry(d, 0, j), fmpz_mat_entry(d, 0, j));
+ }
+ fmpz_sqrtrem(s1, t, s1);
+ if (!fmpz_is_zero(t))
+ fmpz_add_ui(s1, s1, UWORD(1));
+ fmpz_sqrtrem(s2, t, s2);
+ if (!fmpz_is_zero(t))
+ fmpz_add_ui(s2, s2, UWORD(1));
+ fmpz_mul(s1, s1, bound);
+ fmpz_mul(s2, s2, bound);
+ fmpz_cdiv_q(s1, s1, u1);
+ fmpz_cdiv_q(s2, s2, u2);
+ if (fmpz_cmp(s1, s2) > 0)
+ fmpz_set(bound, s1);
+ else
+ fmpz_set(bound, s2);
+ fmpz_mul_ui(bound, bound, UWORD(2));
+
+ fmpz_one(prod);
+ P = _perm_init(n);
+ nmod_mat_init(Btmod, n, n, 2);
+ p = UWORD(1) << NMOD_MAT_OPTIMAL_MODULUS_BITS;
+ /* compute determinants divided by u1 and u2 */
+ while (fmpz_cmp(prod, bound) <= 0)
+ {
+ p = n_nextprime(p, 0);
+ u1mod = fmpz_fdiv_ui(u1, p);
+ u2mod = fmpz_fdiv_ui(u2, p);
+ if (!(u1mod || u2mod))
+ continue;
+ _nmod_mat_set_mod(Btmod, p);
+ for (i = 0; i < n; i++)
+ {
+ for (j = 0; j < n - 1; j++)
+ nmod_mat_entry(Btmod, i, j) =
+ fmpz_fdiv_ui(fmpz_mat_entry(B, j, i), p);
+ nmod_mat_entry(Btmod, i, n - 1) =
+ fmpz_fdiv_ui(fmpz_mat_entry(c, 0, i), p);
+ }
+ nmod_mat_lu(P, Btmod, 0);
+ v1mod = UWORD(1);
+ for (i = 0; i < n; i++)
+ v1mod = n_mulmod2_preinv(v1mod, nmod_mat_entry(Btmod, i, i), p,
+ Btmod->mod.ninv);
+ if (_perm_parity(P, n) == 1)
+ v1mod = nmod_neg(v1mod, Btmod->mod);
+
+ for (i = 0; i < n; i++)
+ {
+ for (j = 0; j < n - 1; j++)
+ nmod_mat_entry(Btmod, i, j) =
+ fmpz_fdiv_ui(fmpz_mat_entry(B, j, i), p);
+ nmod_mat_entry(Btmod, i, n - 1) =
+ fmpz_fdiv_ui(fmpz_mat_entry(d, 0, i), p);
+ }
+ nmod_mat_lu(P, Btmod, 0);
+ v2mod = UWORD(1);
+ for (i = 0; i < n; i++)
+ v2mod = n_mulmod2_preinv(v2mod, nmod_mat_entry(Btmod, i, i), p,
+ Btmod->mod.ninv);
+ if (_perm_parity(P, n) == 1)
+ v2mod = nmod_neg(v2mod, Btmod->mod);
+
+ v1mod = n_mulmod2_preinv(v1mod, n_invmod(u1mod, p), p,
+ Btmod->mod.ninv);
+ v2mod = n_mulmod2_preinv(v2mod, n_invmod(u2mod, p), p,
+ Btmod->mod.ninv);
+ fmpz_CRT_ui(v1, v1, prod, v1mod, p, 1);
+ fmpz_CRT_ui(v2, v2, prod, v2mod, p, 1);
+ fmpz_mul_ui(prod, prod, p);
+ }
+
+ fmpz_mul(d1, u1, v1);
+ fmpz_mul(d2, u2, v2);
+
+ fmpz_clear(bound);
+ fmpz_clear(prod);
+ fmpz_clear(s1);
+ fmpz_clear(s2);
+ fmpz_clear(u1);
+ fmpz_clear(u2);
+ fmpz_clear(v1);
+ fmpz_clear(v2);
+ fmpz_clear(t);
+ _perm_clear(P);
+ nmod_mat_clear(Btmod);
+ }
+ else /* can't use the clever method above so naively compute both dets */
+ {
+ fmpz_mat_det(d1, Bt);
+ for (j = 0; j < n; j++)
+ fmpz_set(fmpz_mat_entry(Bt, j, n - 1), fmpz_mat_entry(d, 0, j));
+ fmpz_mat_det(d2, Bt);
+ }
+
+ fmpz_mat_clear(dt);
+ fmpz_mat_clear(Bt);
+ fmpq_mat_clear(x);
+}
+
+void
+fmpz_mat_hnf_pernet_stein(fmpz_mat_t H, const fmpz_mat_t A, flint_rand_t state)
+{
+ slong i, j, m, n, p, r, *P, *pivots, finished;
+ fmpz_t d1, d2, g, s, t;
+ fmpz_mat_t c, d, B, C, H1, H2, H3;
+ nmod_mat_t Amod;
+
+ m = fmpz_mat_nrows(A);
+ n = fmpz_mat_ncols(A);
+
+ if (m == 0 || n == 0)
+ return;
+
+ /* find permutation so we can ensure first rows of H are nonsingular */
+ P = _perm_init(m);
+ pivots = _perm_init(n);
+
+ finished = 0;
+
+ while (!finished)
+ {
+ p = n_randprime(state, NMOD_MAT_OPTIMAL_MODULUS_BITS, 1);
+ nmod_mat_init(Amod, m, n, p);
+
+ fmpz_mat_get_nmod_mat(Amod, A);
+ r = _nmod_mat_rref(Amod, pivots, P);
+
+ nmod_mat_clear(Amod);
+
+ /* rank is zero so matrix is possibly zero too */
+ if (r == 0)
+ {
+ if (fmpz_mat_is_zero(A))
+ {
+ fmpz_mat_zero(H);
+ _perm_clear(P);
+ _perm_clear(pivots);
+ return;
+ }
+ continue;
+ }
+
+ /* if A has full column rank we might wish to use minors based hnf */
+ if (r == n && n < 52)
+ {
+ slong b = fmpz_mat_max_bits(A), cutoff = 52;
+ if (b < 0)
+ b = -b;
+
+ if (b <= 8)
+ cutoff = 35;
+ else if (b <= 32)
+ cutoff = 44;
+ else if (b <= 256)
+ cutoff = 48;
+
+ if (n < cutoff)
+ {
+ fmpz_mat_hnf_minors(H, A);
+ _perm_clear(P);
+ _perm_clear(pivots);
+ return;
+ }
+ }
+
+ fmpz_mat_init(c, 1, r - 1);
+ fmpz_mat_init(d, 1, r - 1);
+ fmpz_mat_init(B, r - 2, r - 1);
+ fmpz_mat_init(C, r - 1, r - 1);
+
+ for (i = 0; i < r - 2; i++)
+ {
+ for (j = 0; j < r - 1; j++)
+ {
+ fmpz_set(fmpz_mat_entry(B, i, j),
+ fmpz_mat_entry(A, P[i], pivots[j]));
+ fmpz_set(fmpz_mat_entry(C, i, j),
+ fmpz_mat_entry(A, P[i], pivots[j]));
+ }
+ fmpz_set(fmpz_mat_entry(C, i, r - 1),
+ fmpz_mat_entry(A, P[i], pivots[r - 1]));
+ }
+ for (j = 0; j < r - 1; j++)
+ {
+ fmpz_set(fmpz_mat_entry(c, 0, j),
+ fmpz_mat_entry(A, P[r - 2], pivots[j]));
+ fmpz_set(fmpz_mat_entry(d, 0, j),
+ fmpz_mat_entry(A, P[r - 1], pivots[j]));
+ }
+
+ fmpz_init(g);
+ fmpz_init(s);
+ fmpz_init(t);
+
+ /* if rank is too low leave g = 0 so we don't try to decompose later */
+ if (r > 2)
+ {
+ fmpz_init(d1);
+ fmpz_init(d2);
+
+ double_det(d1, d2, B, c, d);
+ fmpz_xgcd(g, s, t, d1, d2);
+
+ for (j = 0; j < r - 1; j++)
+ {
+ fmpz_mul(fmpz_mat_entry(C, r - 2, j), s,
+ fmpz_mat_entry(A, P[r - 2], pivots[j]));
+ fmpz_addmul(fmpz_mat_entry(C, r - 2, j), t,
+ fmpz_mat_entry(A, P[r - 1], pivots[j]));
+ }
+
+ fmpz_clear(d2);
+ fmpz_clear(d1);
+ }
+
+ if (!fmpz_is_zero(g)) /* chosen matrix invertible */
+ {
+ fmpz_mat_init(H1, r - 1, r - 1);
+
+ if (COEFF_IS_MPZ(*g) && C->r > 3) /* if g is too big, recurse */
+ fmpz_mat_hnf_pernet_stein(H1, C, state);
+ else /* use modulo determinant algorithm to compute HNF of C */
+ fmpz_mat_hnf_modular(H1, C, g);
+
+ fmpz_mat_clear(B);
+ fmpz_mat_init(B, r - 1, n);
+
+ for (j = 0; j < n; j++)
+ {
+ for (i = 0; i < r - 2; i++)
+ fmpz_set(fmpz_mat_entry(B, i, j),
+ fmpz_mat_entry(A, P[i], pivots[j]));
+ fmpz_mul(fmpz_mat_entry(B, r - 2, j), s,
+ fmpz_mat_entry(A, P[r - 2], pivots[j]));
+ fmpz_addmul(fmpz_mat_entry(B, r - 2, j), t,
+ fmpz_mat_entry(A, P[r - 1], pivots[j]));
+ }
+
+ fmpz_mat_init(H2, r - 1, n);
+ fmpz_mat_init(H3, m + 1, n);
+
+ add_columns(H2, B, H1, state);
+
+ for (i = 0; i < r - 1; i++)
+ for (j = 0; j < n; j++)
+ fmpz_set(fmpz_mat_entry(H3, i, pivots[j]),
+ fmpz_mat_entry(H2, i, j));
+
+ for (i = 1; i <= m - r + 2; i++)
+ for (j = 0; j < n; j++)
+ fmpz_set(fmpz_mat_entry(H3, H3->r - i, j),
+ fmpz_mat_entry(A, P[m - i], j));
+
+ /* check the pivots of H3 are as expected */
+ for (i = 0; i < r - 1; i++)
+ {
+ for (j = 0; j < H3->c && fmpz_is_zero(fmpz_mat_entry(H3, i, j)); j++);
+ if (pivots[i] != j)
+ break;
+ }
+
+ /* the pivots were as expected so our choice of prime was ok */
+ if (i == r - 1)
+ {
+ /* add final rows in */
+ add_rows(H3, r - 1, pivots, r - 1);
+
+ /* fill H with HNF */
+ for (i = 0; i < m; i++)
+ for (j = 0; j < n; j++)
+ fmpz_set(fmpz_mat_entry(H, i, j), fmpz_mat_entry(H3, i, j));
+
+ finished = 1;
+ }
+ /* otherwise we must restart as our random prime gave us incorrect pivots */
+
+ fmpz_mat_clear(H1);
+ fmpz_mat_clear(H2);
+ fmpz_mat_clear(H3);
+ }
+ else
+ {
+ if (r == n) /* if A has full column rank we can use minors based hnf */
+ fmpz_mat_hnf_minors(H, A);
+ else
+ fmpz_mat_hnf_classical(H, A);
+ finished = 1;
+ }
+
+ fmpz_clear(t);
+ fmpz_clear(s);
+ fmpz_clear(g);
+ fmpz_mat_clear(C);
+ fmpz_mat_clear(B);
+ fmpz_mat_clear(c);
+ fmpz_mat_clear(d);
+ }
+
+ _perm_clear(P);
+ _perm_clear(pivots);
+}
+
+#endif
\ No newline at end of file
diff --git a/fmpz_sparse_mat/hnf_xgcd.c b/fmpz_sparse_mat/hnf_xgcd.c
new file mode 100644
index 0000000000..7d765057fb
--- /dev/null
+++ b/fmpz_sparse_mat/hnf_xgcd.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+slong fmpz_sparse_mat_hnf_xgcd(fmpz_sparse_mat_t M)
+{
+ slong i, r, pr, pc, rank, remr, nnz;
+ slong *P; /* Row permutation */
+ slong *irows; /* Set of rows incident on a given column */
+ slong nnp; /* Number of rows which are not pivots */
+ slong npp; /* Number of rows which are previous pivots */
+ fmpz_sparse_mat_with_transpose_t MT;
+ hashmap_struct *hcol; /* Virtual L^t and one of its rows */
+ fmpz_t g, a, b, one;
+
+ if (M->r == 0 || M->c == 0) return 0;
+ fmpz_init(g);
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_init_set_ui(one, UWORD(1));
+
+ /* Construct virtual transpose */
+ _fmpz_sparse_mat_with_transpose_init(MT, M);
+
+ /* Set up permutation */
+ P = flint_malloc(M->r*sizeof(*P));
+ remr = M->r;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+ irows = flint_malloc(M->r * sizeof(*irows));
+
+ for (rank = pc = 0; pc < M->c; ++pc)
+ {
+ hcol = &MT->cols[pc]; nnz = hcol->num;
+ if (!nnz) continue;
+ pr = -1, nnp = 0, npp = 0;
+
+ for (i = 0; i < nnz; ++i)
+ {
+ r = hcol->keys[i];
+ if (P[r] >= 0) /* Put previous pivot rows at end of irows */
+ irows[hcol->num - (++npp)] = r;
+ else if (pr >= 0 && fmpz_cmpabs(LT(M, r).val, LT(M, pr).val) >= 0)
+ irows[nnp++] = r; /* Put non-pivot rows with larger entries at beginning of irows */
+ else /* Replace the pivot row with the current row */
+ {
+ if (pr >= 0) irows[nnp++] = pr;
+ pr = r;
+ }
+ }
+ if (pr == -1) continue;
+
+ /* Eliminate larger, non-pivot rows */
+ for (i = 0; i < nnp; ++i)
+ {
+ r = irows[i];
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext(MT, pr, r);
+ if (M->rows[r].nnz == 0) P[r] = --remr;
+ }
+
+ if (fmpz_sgn(LT(M, pr).val) < 0)
+ fmpz_sparse_vec_neg(&M->rows[pr], &M->rows[pr]);
+
+ /* Reduce previous pivot rows incident to pivot column */
+ for (i = nnz - npp; i < nnz; ++i)
+ _fmpz_sparse_mat_with_transpose_gauss_elim(MT, pr, irows[i]);
+ P[pr] = rank++;
+ }
+
+ /* Apply row permutation */
+ fmpz_sparse_mat_permute_rows (M, P);
+
+ flint_free(P);
+ flint_free(irows);
+ fmpz_clear(a);
+ fmpz_clear(b);
+ fmpz_clear(one);
+ _fmpz_sparse_mat_with_transpose_clear(MT);
+ return rank;
+}
diff --git a/fmpz_sparse_mat/howell_form_mod.c b/fmpz_sparse_mat/howell_form_mod.c
new file mode 100644
index 0000000000..e07c84a7ad
--- /dev/null
+++ b/fmpz_sparse_mat/howell_form_mod.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2015 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+slong
+fmpz_sparse_mat_howell_form_mod(fmpz_sparse_mat_t M, const fmpz_t mod)
+{
+ slong i, *P, rank = 0, remr = M->r;
+
+ if (fmpz_sparse_mat_is_zero(M)) return 0;
+
+ fmpz_sparse_mat_strong_echelon_form_mod(M, mod);
+ P = flint_malloc(M->r*sizeof(*P));
+ for (i = 0; i < M->r; ++i)
+ {
+ if (M->rows[i].nnz > 0) P[i] = rank++;
+ else P[i] = --remr;
+ }
+ /* Apply row permutation */
+ fmpz_sparse_mat_permute_rows (M, P);
+ flint_free(P);
+ return rank;
+}
+
diff --git a/fmpz_sparse_mat/is_in_hnf.c b/fmpz_sparse_mat/is_in_hnf.c
new file mode 100644
index 0000000000..9caa46df77
--- /dev/null
+++ b/fmpz_sparse_mat/is_in_hnf.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+int fmpz_sparse_mat_is_in_hnf(const fmpz_sparse_mat_t A)
+{
+ slong pr, rk, pc, prev_pc, r;
+ fmpz_t *ptr;
+
+ /* Compute the rank (assuming HNF form) */
+ for (rk = A->r; rk != 0; rk--)
+ {
+ if (!fmpz_sparse_vec_is_zero(&A->rows[rk - 1]) && LT(A, rk - 1).ind < A->c) break;
+ }
+
+ /* Check that first rk rows are in HNF */
+ prev_pc = -1;
+ for (pr = 0; pr < rk; pr++)
+ {
+ if (fmpz_sparse_vec_is_zero(&A->rows[pr])) return 0;
+ pc = LT(A, pr).ind;
+ if (pc >= A->c || pc <= prev_pc || fmpz_sgn(LT(A, pr).val) < 0) return 0;
+
+ for (r = 0; r < pr; r++)
+ {
+ ptr = fmpz_sparse_vec_at(&A->rows[r], pc);
+ if (ptr && ((fmpz_sgn(*ptr) < 0) || (fmpz_cmp(*ptr, LT(A, pr).val) >= 0))) return 0;
+ }
+ prev_pc = pc;
+ }
+
+ return 1;
+}
diff --git a/fmpz_sparse_mat/max_bits.c b/fmpz_sparse_mat/max_bits.c
new file mode 100644
index 0000000000..d5337efa80
--- /dev/null
+++ b/fmpz_sparse_mat/max_bits.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "hashmap.h"
+#include "longlong.h"
+
+slong fmpz_sparse_mat_max_bits(const fmpz_sparse_mat_t M)
+{
+ slong i;
+ slong bits, row_bits, sign;
+
+ sign = 1;
+ bits = 0;
+
+ if (M->r == 0 || M->c == 0)
+ return 0;
+
+ for (i = 0; i < M->r; i++)
+ {
+ row_bits = fmpz_sparse_vec_max_bits(&M->rows[i]);
+ if (row_bits < 0)
+ {
+ row_bits = -row_bits;
+ sign = -1;
+ }
+ bits = FLINT_MAX(bits, row_bits);
+ }
+ return bits * sign;
+}
\ No newline at end of file
diff --git a/fmpz_sparse_mat/multi_CRT_ui.c b/fmpz_sparse_mat/multi_CRT_ui.c
new file mode 100644
index 0000000000..ee8bfdbdcf
--- /dev/null
+++ b/fmpz_sparse_mat/multi_CRT_ui.c
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_multi_CRT_ui_precomp(fmpz_sparse_mat_t M,
+ nmod_sparse_mat_struct * residues, slong nres,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp, int sign)
+{
+ slong i, j;
+ nmod_sparse_vec_struct *vres;
+
+ vres = flint_malloc(nres * sizeof(*vres));
+
+ for (i = 0; i < M->r; i++)
+ {
+ for (j = 0; j < nres; ++j) vres[j] = residues[j].rows[i];
+ fmpz_sparse_vec_multi_CRT_ui_precomp(&M->rows[i], vres, nres, comb, temp, sign);
+ }
+ flint_free(vres);
+}
+
+void
+fmpz_sparse_mat_multi_CRT_ui(fmpz_sparse_mat_t mat, nmod_sparse_mat_struct * residues, slong nres, int sign)
+{
+ slong i;
+ mp_limb_t *primes;
+ fmpz_comb_t comb;
+ fmpz_comb_temp_t temp;
+
+ for(i = 0; i < nres; ++i) {
+ FLINT_ASSERT(mat->r == residues[i].r);
+ }
+
+ primes = flint_malloc(nres * sizeof(*primes));
+ for (i = 0; i < nres; i++) primes[i] = residues[i].mod.n;
+ fmpz_comb_init(comb, primes, nres);
+ fmpz_comb_temp_init(temp, comb);
+
+ fmpz_sparse_mat_multi_CRT_ui_precomp(mat, residues, nres, comb, temp, sign);
+
+ fmpz_comb_clear(comb);
+ fmpz_comb_temp_clear(temp);
+ flint_free(primes);
+}
diff --git a/fmpz_sparse_mat/multi_mod_ui.c b/fmpz_sparse_mat/multi_mod_ui.c
new file mode 100644
index 0000000000..28aa8ec705
--- /dev/null
+++ b/fmpz_sparse_mat/multi_mod_ui.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+/* for (j = 0; j < fmpz_mat_ncols(mat); j++)
+ {
+ fmpz_multi_mod_ui(r, fmpz_mat_entry(mat, i, j), comb, temp);
+ for (k = 0; k < nres; k++)
+ nmod_mat_entry(residues[k], i, j) = r[k];
+ }
+ */
+void
+fmpz_sparse_mat_multi_mod_ui_precomp(nmod_sparse_mat_struct * residues, slong nres, const fmpz_sparse_mat_t M,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp)
+{
+ slong i, j;
+ nmod_sparse_vec_struct *vres;
+
+ vres = flint_malloc(nres * sizeof(*vres));
+
+ for (i = 0; i < M->r; i++)
+ {
+ for (j = 0; j < nres; ++j) vres[j] = residues[j].rows[i];
+ fmpz_sparse_vec_multi_mod_ui_precomp(vres, nres, &M->rows[i], comb, temp);
+ for (j = 0; j < nres; ++j) residues[j].rows[i] = vres[j];
+
+ }
+ flint_free(vres);
+}
+
+void
+fmpz_sparse_mat_multi_mod_ui(nmod_sparse_mat_struct * residues, slong nres, const fmpz_sparse_mat_t M)
+{
+ slong i;
+ for(i = 0; i < nres; ++i) {
+ FLINT_ASSERT(mat->r == residues[i].r);
+ }
+ mp_limb_t *primes;
+ fmpz_comb_t comb;
+ fmpz_comb_temp_t temp;
+
+ primes = flint_malloc(nres * sizeof(*primes));
+ for (i = 0; i < nres; i++) primes[i] = residues[i].mod.n;
+ fmpz_comb_init(comb, primes, nres);
+ fmpz_comb_temp_init(temp, comb);
+
+ fmpz_sparse_mat_multi_mod_ui_precomp(residues, nres, M, comb, temp);
+
+ fmpz_comb_clear(comb);
+ fmpz_comb_temp_clear(temp);
+ flint_free(primes);
+}
diff --git a/fmpz_sparse_mat/print_pretty.c b/fmpz_sparse_mat/print_pretty.c
new file mode 100644
index 0000000000..a2c4dcfd07
--- /dev/null
+++ b/fmpz_sparse_mat/print_pretty.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_print_pretty(const fmpz_sparse_mat_t M)
+{
+ slong i;
+ char row_fmt[FLINT_BITS + 5];
+ flint_sprintf(row_fmt, "%%%dwd: ", n_sizeinbase(M->r, 10));
+
+ flint_printf("<%wd x %wd sparse integer matrix>\n",
+ M->r, M->c);
+
+ for (i = 0; i < M->r; i++)
+ {
+ flint_printf(row_fmt, i);
+ fmpz_sparse_vec_print_pretty(&M->rows[i], M->c_off, M->c);
+ }
+}
diff --git a/fmpz_sparse_mat/randtest.c b/fmpz_sparse_mat/randtest.c
new file mode 100644
index 0000000000..1c7a184925
--- /dev/null
+++ b/fmpz_sparse_mat/randtest.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_randtest(fmpz_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, flint_bitcnt_t bits)
+{
+ slong i, nnz;
+
+ for (i = 0; i < M->r; ++i)
+ {
+ nnz = n_randint(state, max_nnz+1);
+ nnz = FLINT_MAX(nnz, min_nnz);
+ fmpz_sparse_vec_randtest(&M->rows[i], state, nnz, M->c, bits);
+ }
+}
+
diff --git a/fmpz_sparse_mat/randtest_unsigned.c b/fmpz_sparse_mat/randtest_unsigned.c
new file mode 100644
index 0000000000..06fdb1d90d
--- /dev/null
+++ b/fmpz_sparse_mat/randtest_unsigned.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_randtest_unsigned(fmpz_sparse_mat_t M, flint_rand_t state, slong min_nnz, slong max_nnz, flint_bitcnt_t bits)
+{
+ slong i, nnz;
+
+ for (i = 0; i < M->r; ++i)
+ {
+ nnz = n_randint(state, max_nnz+1);
+ nnz = FLINT_MAX(nnz, min_nnz);
+ fmpz_sparse_vec_randtest_unsigned(&M->rows[i], state, nnz, M->c, bits);
+ }
+}
+
diff --git a/fmpz_sparse_mat/snf.c b/fmpz_sparse_mat/snf.c
new file mode 100644
index 0000000000..f87b4e3d45
--- /dev/null
+++ b/fmpz_sparse_mat/snf.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_mat.h"
+
+void
+fmpz_sparse_mat_snf(fmpz_mat_t S, const fmpz_mat_t A)
+{
+ fmpz_t det;
+ slong m = A->r, n = A->c, b = fmpz_mat_max_bits(A), cutoff = 9;
+
+ if (b <= 2)
+ cutoff = 15;
+ else if (b <= 4)
+ cutoff = 13;
+ else if (b <= 8)
+ cutoff = 13;
+ else if (b <= 16)
+ cutoff = 11;
+ else if (b <= 32)
+ cutoff = 11;
+ else if (b <= 64)
+ cutoff = 10;
+
+ if (FLINT_MAX(m, n) < cutoff || m != n)
+ fmpz_mat_snf_kannan_bachem(S, A);
+ else
+ {
+ fmpz_init(det);
+ fmpz_mat_det(det, A);
+ if (!fmpz_is_zero(det))
+ {
+ fmpz_abs(det, det);
+ fmpz_mat_snf_iliopoulos(S, A, det);
+ }
+ else
+ {
+ fmpz_mat_snf_kannan_bachem(S, A);
+ }
+ fmpz_clear(det);
+ }
+}
diff --git a/fmpz_sparse_mat/snf_diagonal.c b/fmpz_sparse_mat/snf_diagonal.c
new file mode 100644
index 0000000000..b4e3dc2703
--- /dev/null
+++ b/fmpz_sparse_mat/snf_diagonal.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_mat.h"
+
+/* sets a to gcd(a,b) and b to lcm(a,b) using temporary fmpz_t t */
+static void _gcdlcm(fmpz_t t, fmpz_t a, fmpz_t b)
+{
+ if (fmpz_equal(a, b)) return;
+ fmpz_gcd(t, a, b);
+ fmpz_divexact(b, b, t);
+ fmpz_mul(b, b, a);
+ fmpz_set(a, t);
+}
+
+void fmpz_sparse_mat_snf_diagonal(fmpz_mat_t S, const fmpz_mat_t A)
+{
+ fmpz_t t;
+ slong i, j, n = FLINT_MIN(A->r, A->c);
+
+ fmpz_init(t);
+ fmpz_mat_set(S, A);
+ for (i = 0; i < n; i++)
+ fmpz_abs(fmpz_mat_entry(S, i, i), fmpz_mat_entry(S, i, i));
+ for (j = n - 1; j >= 0; j--)
+ {
+ for (i = 0; i < j; i++)
+ {
+ _gcdlcm(t, fmpz_mat_entry(S, i, i),
+ fmpz_mat_entry(S, i + 1, i + 1));
+ }
+ }
+ fmpz_clear(t);
+}
diff --git a/fmpz_sparse_mat/snf_iliopoulos.c b/fmpz_sparse_mat/snf_iliopoulos.c
new file mode 100644
index 0000000000..64abb62250
--- /dev/null
+++ b/fmpz_sparse_mat/snf_iliopoulos.c
@@ -0,0 +1,238 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_mat.h"
+
+static void _eliminate_col(fmpz_mat_t S, slong i, const fmpz_t mod)
+{
+ slong j, k, m, n;
+ fmpz * t;
+ fmpz_t b, g, u, v, r1g, r2g;
+
+ m = S->r;
+ n = S->c;
+
+ if (i == m - 1)
+ {
+ fmpz_gcd(fmpz_mat_entry(S, i, i), fmpz_mat_entry(S, i, i), mod);
+ return;
+ }
+
+ fmpz_init(g);
+ fmpz_init(u);
+ fmpz_init(b);
+ fmpz_init(r1g);
+ fmpz_init(r2g);
+
+ if (!fmpz_is_zero(fmpz_mat_entry(S, i, i)))
+ {
+ fmpz_init(v);
+
+ fmpz_xgcd(g, u, v, fmpz_mat_entry(S, i + 1, i),
+ fmpz_mat_entry(S, i, i));
+ fmpz_divexact(r1g, fmpz_mat_entry(S, i + 1, i), g);
+ fmpz_divexact(r2g, fmpz_mat_entry(S, i, i), g);
+ for (j = i; j < n; j++)
+ {
+ fmpz_mul(b, u, fmpz_mat_entry(S, i + 1, j));
+ fmpz_addmul(b, v, fmpz_mat_entry(S, i, j));
+ fmpz_mul(fmpz_mat_entry(S, i, j), r1g,
+ fmpz_mat_entry(S, i, j));
+ fmpz_submul(fmpz_mat_entry(S, i, j), r2g,
+ fmpz_mat_entry(S, i + 1, j));
+ fmpz_set(fmpz_mat_entry(S, i + 1, j), b);
+ }
+
+ fmpz_clear(v);
+ }
+
+ /* compute extended gcd of entries in column i */
+ t = _fmpz_vec_init(m - i - 1);
+
+ fmpz_set(g, fmpz_mat_entry(S, i + 1, i));
+ fmpz_one(t);
+ for (j = 2; j < m - i; j++)
+ {
+ fmpz_xgcd(g, u, t + j - 1, g, fmpz_mat_entry(S, i + j, i));
+ for (k = 0; k < j - 1; k++)
+ fmpz_mul(t + k, t + k, u);
+ }
+
+ /* set row i to have gcd in col i */
+ for (k = i + 1; k < m; k++)
+ {
+ fmpz_mod(t + k - i - 1, t + k - i - 1, mod);
+ for (j = i; j < n; j++)
+ fmpz_addmul(fmpz_mat_entry(S, i, j), t + k - i - 1,
+ fmpz_mat_entry(S, k, j));
+ }
+
+ _fmpz_vec_clear(t, m - i - 1);
+
+ /* reduce each row k with row i */
+ if (!fmpz_is_zero(g)) /* if g = 0 then don't need to reduce */
+ {
+ for (k = i + 1; k < m; k++)
+ {
+ fmpz_divexact(r1g, fmpz_mat_entry(S, k, i), g);
+ fmpz_neg(r1g, r1g);
+ for (j = i; j < n; j++)
+ fmpz_addmul(fmpz_mat_entry(S, k, j), r1g,
+ fmpz_mat_entry(S, i, j));
+ }
+ for (k = i + 1; k < m; k++)
+ fmpz_mod(fmpz_mat_entry(S, k, i), fmpz_mat_entry(S, k, i), mod);
+ }
+ for (j = i; j < m; j++)
+ for (k = i + 1; k < n; k++)
+ fmpz_fdiv_r(fmpz_mat_entry(S, j, k), fmpz_mat_entry(S, j, k), mod);
+ fmpz_gcd(fmpz_mat_entry(S, i, i), fmpz_mat_entry(S, i, i), mod);
+
+ fmpz_clear(b);
+ fmpz_clear(g);
+ fmpz_clear(u);
+ fmpz_clear(r1g);
+ fmpz_clear(r2g);
+}
+
+static void _eliminate_row(fmpz_mat_t S, slong i, const fmpz_t mod)
+{
+ slong j, k, m, n;
+ fmpz * t;
+ fmpz_t b, g, u, v, r1g, r2g, halfmod;
+
+ m = S->r;
+ n = S->c;
+
+ if (i == n - 1)
+ {
+ fmpz_gcd(fmpz_mat_entry(S, i, i), fmpz_mat_entry(S, i, i), mod);
+ return;
+ }
+
+ fmpz_init(g);
+ fmpz_init(u);
+ fmpz_init(b);
+ fmpz_init(r1g);
+ fmpz_init(r2g);
+ fmpz_init(halfmod);
+ fmpz_fdiv_q_2exp(halfmod, mod, 1);
+
+ if (!fmpz_is_zero(fmpz_mat_entry(S, i, i)))
+ {
+ fmpz_init(v);
+
+ fmpz_xgcd(g, u, v, fmpz_mat_entry(S, i, i + 1),
+ fmpz_mat_entry(S, i, i));
+ fmpz_divexact(r1g, fmpz_mat_entry(S, i, i + 1), g);
+ fmpz_divexact(r2g, fmpz_mat_entry(S, i, i), g);
+ for (j = i; j < m; j++)
+ {
+ fmpz_mul(b, u, fmpz_mat_entry(S, j, i + 1));
+ fmpz_addmul(b, v, fmpz_mat_entry(S, j, i));
+ fmpz_mul(fmpz_mat_entry(S, j, i), r1g,
+ fmpz_mat_entry(S, j, i));
+ fmpz_submul(fmpz_mat_entry(S, j, i), r2g,
+ fmpz_mat_entry(S, j, i + 1));
+ fmpz_set(fmpz_mat_entry(S, j, i + 1), b);
+ }
+
+ fmpz_clear(v);
+ }
+
+ /* compute extended gcd of entries in row i */
+ t = _fmpz_vec_init(n - i - 1);
+
+ fmpz_set(g, fmpz_mat_entry(S, i, i + 1));
+ fmpz_one(t);
+ for (j = 2; j < n - i; j++)
+ {
+ fmpz_xgcd(g, u, t + j - 1, g, fmpz_mat_entry(S, i, i + j));
+ for (k = 0; k < j - 1; k++)
+ fmpz_mul(t + k, t + k, u);
+ }
+
+ /* reduce col i to have gcd in row i */
+ for (k = i + 1; k < n; k++)
+ {
+ fmpz_mod(t + k - i - 1, t + k - i - 1, mod);
+ for (j = i; j < m; j++)
+ fmpz_addmul(fmpz_mat_entry(S, j, i), t + k - i - 1,
+ fmpz_mat_entry(S, j, k));
+ }
+
+ _fmpz_vec_clear(t, n - i - 1);
+
+ /* reduce each col k with col i */
+ if (!fmpz_is_zero(g)) /* if g = 0 then don't need to reduce */
+ {
+ for (k = i + 1; k < n; k++)
+ {
+ fmpz_divexact(r1g, fmpz_mat_entry(S, i, k), g);
+ fmpz_neg(r1g, r1g);
+ for (j = i; j < m; j++)
+ fmpz_addmul(fmpz_mat_entry(S, j, k), r1g,
+ fmpz_mat_entry(S, j, i));
+ }
+ }
+ for (j = i + 1; j < m; j++)
+ for (k = i; k < n; k++)
+ fmpz_fdiv_r(fmpz_mat_entry(S, j, k), fmpz_mat_entry(S, j, k), mod);
+ fmpz_gcd(fmpz_mat_entry(S, i, i), fmpz_mat_entry(S, i, i), mod);
+
+ fmpz_clear(b);
+ fmpz_clear(g);
+ fmpz_clear(u);
+ fmpz_clear(r1g);
+ fmpz_clear(r2g);
+ fmpz_clear(halfmod);
+}
+
+void fmpz_sparse_mat_snf_iliopoulos(fmpz_mat_t S, const fmpz_mat_t A, const fmpz_t mod)
+{
+ slong i, k, n;
+ int done;
+
+ n = FLINT_MIN(A->c, A->r);
+
+ fmpz_mat_set(S, A);
+
+ for (i = 0; i < A->r; i++)
+ for (k = 0; k < A->c; k++)
+ fmpz_mod(fmpz_mat_entry(S, i, k), fmpz_mat_entry(S, i, k), mod);
+
+ for (k = 0; k != n; k++)
+ {
+ do
+ {
+ _eliminate_row(S, k, mod);
+ _eliminate_col(S, k, mod);
+ done = 1;
+ if (fmpz_is_zero(fmpz_mat_entry(S, k, k)))
+ {
+ for (i = k + 1; i < A->c && done; i++)
+ done = fmpz_is_zero(fmpz_mat_entry(S, k, i));
+ }
+ else
+ {
+ for (i = k + 1; i < A->c && done; i++)
+ done = fmpz_divisible(fmpz_mat_entry(S, k, i),
+ fmpz_mat_entry(S, k, k));
+ }
+ }
+ while (!done);
+ for (i = k + 1; i < A->c; i++)
+ fmpz_zero(fmpz_mat_entry(S, k, i));
+ }
+
+ fmpz_mat_snf_diagonal(S, S);
+}
diff --git a/fmpz_sparse_mat/snf_kannan_bachem.c b/fmpz_sparse_mat/snf_kannan_bachem.c
new file mode 100644
index 0000000000..9851416d07
--- /dev/null
+++ b/fmpz_sparse_mat/snf_kannan_bachem.c
@@ -0,0 +1,138 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_mat.h"
+
+void fmpz_sparse_mat_snf_kannan_bachem(fmpz_mat_t S, const fmpz_mat_t A)
+{
+ slong i, j, k, d, m, n;
+ fmpz_t r1g, r2g, b, u, v, g;
+ m = A->r;
+ n = A->c;
+ d = FLINT_MIN(m, n);
+ fmpz_init(r1g);
+ fmpz_init(r2g);
+ fmpz_init(b);
+ fmpz_init(u);
+ fmpz_init(v);
+ fmpz_init(g);
+
+ fmpz_mat_set(S, A);
+
+ for (k = 0; k != d; k++)
+ {
+ int col_done;
+ do
+ {
+ /* clear column */
+ for (i = k + 1; i != m; i++)
+ {
+ /* reduce row i - 1 with row i */
+ if (fmpz_is_zero(fmpz_mat_entry(S, i - 1, k)))
+ continue;
+ if (fmpz_cmpabs(fmpz_mat_entry(S, i, k),
+ fmpz_mat_entry(S, i - 1, k)) == 0)
+ {
+ if (fmpz_equal(fmpz_mat_entry(S, i, k),
+ fmpz_mat_entry(S, i - 1, k)))
+ {
+ for (j = k; j != n; j++)
+ fmpz_sub(fmpz_mat_entry(S, i - 1, j),
+ fmpz_mat_entry(S, i - 1, j),
+ fmpz_mat_entry(S, i, j));
+ }
+ else
+ {
+ for (j = k; j != n; j++)
+ fmpz_add(fmpz_mat_entry(S, i - 1, j),
+ fmpz_mat_entry(S, i - 1, j),
+ fmpz_mat_entry(S, i, j));
+ }
+ continue;
+ }
+ fmpz_xgcd(g, u, v, fmpz_mat_entry(S, i, k),
+ fmpz_mat_entry(S, i - 1, k));
+ fmpz_divexact(r2g, fmpz_mat_entry(S, i - 1, k), g);
+ fmpz_divexact(r1g, fmpz_mat_entry(S, i, k), g);
+ for (j = k; j != n; j++)
+ {
+ fmpz_mul(b, u, fmpz_mat_entry(S, i, j));
+ fmpz_addmul(b, v, fmpz_mat_entry(S, i - 1, j));
+ fmpz_mul(fmpz_mat_entry(S, i - 1, j), r1g,
+ fmpz_mat_entry(S, i - 1, j));
+ fmpz_submul(fmpz_mat_entry(S, i - 1, j), r2g,
+ fmpz_mat_entry(S, i, j));
+ fmpz_set(fmpz_mat_entry(S, i, j), b);
+ }
+ }
+ fmpz_mat_swap_rows(S, NULL, m - 1, k);
+
+ /* clear row */
+ for (j = k + 1; j != n; j++)
+ {
+ /* reduce col j with col k */
+ if (fmpz_is_zero(fmpz_mat_entry(S, k, j)))
+ continue;
+ if (fmpz_cmpabs(fmpz_mat_entry(S, k, k),
+ fmpz_mat_entry(S, k, j)) == 0)
+ {
+ if (fmpz_equal(fmpz_mat_entry(S, k, k),
+ fmpz_mat_entry(S, k, j)))
+ {
+ for (i = k; i != m; i++)
+ fmpz_sub(fmpz_mat_entry(S, i, j),
+ fmpz_mat_entry(S, i, j),
+ fmpz_mat_entry(S, i, k));
+ }
+ else
+ {
+ for (i = k; i != m; i++)
+ fmpz_add(fmpz_mat_entry(S, i, j),
+ fmpz_mat_entry(S, i, j),
+ fmpz_mat_entry(S, i, k));
+ }
+ continue;
+ }
+ fmpz_xgcd(g, u, v, fmpz_mat_entry(S, k, k),
+ fmpz_mat_entry(S, k, j));
+ fmpz_divexact(r2g, fmpz_mat_entry(S, k, j), g);
+ fmpz_divexact(r1g, fmpz_mat_entry(S, k, k), g);
+ for (i = k; i != m; i++)
+ {
+ fmpz_mul(b, u, fmpz_mat_entry(S, i, k));
+ fmpz_addmul(b, v, fmpz_mat_entry(S, i, j));
+ fmpz_mul(fmpz_mat_entry(S, i, j), r1g,
+ fmpz_mat_entry(S, i, j));
+ fmpz_submul(fmpz_mat_entry(S, i, j), r2g,
+ fmpz_mat_entry(S, i, k));
+ fmpz_set(fmpz_mat_entry(S, i, k), b);
+ }
+ }
+ col_done = 1;
+ for (i = 0; i != m; i++)
+ col_done &= (i == k) || fmpz_is_zero(fmpz_mat_entry(S, i, k));
+ }
+ while (!col_done);
+
+ if (fmpz_sgn(fmpz_mat_entry(S, k, k)) < 0)
+ fmpz_neg(fmpz_mat_entry(S, k, k), fmpz_mat_entry(S, k, k));
+ }
+
+ fmpz_clear(r2g);
+ fmpz_clear(r1g);
+ fmpz_clear(b);
+ fmpz_clear(u);
+ fmpz_clear(v);
+ fmpz_clear(g);
+
+ fmpz_mat_snf_diagonal(S, S);
+}
diff --git a/fmpz_sparse_mat/solve_bound.c b/fmpz_sparse_mat/solve_bound.c
new file mode 100644
index 0000000000..cec4ae9b34
--- /dev/null
+++ b/fmpz_sparse_mat/solve_bound.c
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_solve_bound(fmpz_t N, fmpz_t D,
+ const fmpz_sparse_mat_t A, const fmpz_mat_t B)
+{
+ slong i, j;
+ fmpz_t t, u;
+
+ /* Get product of row norms of A */
+ fmpz_sparse_mat_det_bound(D, A);
+
+ fmpz_init(t);
+ fmpz_init(u);
+
+ fmpz_zero(t);
+
+ /* Get largest column norm of B */
+ for (j = 0; j < B->c; j++)
+ {
+ fmpz_zero(u);
+ for (i = 0; i < A->r; i++)
+ fmpz_addmul(u, fmpz_mat_entry(B, i, j), fmpz_mat_entry(B, i, j));
+ if (fmpz_cmp(t, u) < 0)
+ fmpz_set(t, u);
+ }
+ fmpz_sqrtrem(t, u, t);
+ if (!fmpz_is_zero(u))
+ fmpz_add_ui(t, t, UWORD(1));
+
+ fmpz_mul(N, D, t);
+
+ fmpz_clear(t);
+ fmpz_clear(u);
+}
diff --git a/fmpz_sparse_mat/solve_dixon.c b/fmpz_sparse_mat/solve_dixon.c
new file mode 100644
index 0000000000..8532d6a54d
--- /dev/null
+++ b/fmpz_sparse_mat/solve_dixon.c
@@ -0,0 +1,236 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpq_mat.h"
+#include "fmpz_sparse_mat.h"
+
+static mp_limb_t find_good_prime_and_invert(nmod_sparse_mat_t Ainv, const fmpz_sparse_mat_t A, const fmpz_t D)
+{
+ nmod_t nmod;
+ slong rk = 0;
+ mp_limb_t p;
+ fmpz_t prod;
+ fmpz_init(prod);
+
+ /* Find a prime to use for Hensel lifting, i.e., one modulo which A is invertible */
+ p = n_nextprime(UWORD(1) << NMOD_MAT_OPTIMAL_MODULUS_BITS, 0);
+ for (fmpz_one(prod); fmpz_cmp(prod, D) <= 0; fmpz_mul_ui(prod, prod, p), p = n_nextprime(p, 0))
+ {
+ nmod_init(&nmod, p);
+ nmod_sparse_mat_init(Ainv, A->r, A->r, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(Ainv, A);
+ rk = nmod_sparse_mat_inv(Ainv, Ainv);
+ if (rk == A->r) break;
+ nmod_sparse_mat_clear(Ainv);
+ }
+ if (rk != A->r) p = 0; /* Failed to invert matrix modulo prime == probably not invertible */
+ fmpz_clear(prod);
+ return p;
+}
+
+static nmod_sparse_mat_struct * get_mod_mats(slong *num_primes, const fmpz_sparse_mat_t A, mp_limb_t p)
+{
+ slong np;
+ nmod_t nmod;
+ fmpz_t prod, bound;
+ nmod_sparse_mat_struct *A_mod;
+ fmpz_init(prod);
+
+ /* Get bound on product of primes needed to recover Ay via CRT */
+ fmpz_init_set_ui(bound, p); /* size of y */
+ fmpz_mul_2exp(bound, bound, FLINT_ABS(fmpz_sparse_mat_max_bits(A))+1); /* Size of A + sign */
+ fmpz_mul_ui(bound, bound, A->r); /* Entries in dot product */
+
+ np = fmpz_bits(bound) / (FLINT_BIT_COUNT(p) - 1) + 2; /* Overestimate */
+ A_mod = flint_malloc(np * sizeof(*A_mod));
+ np = 0;
+ for (fmpz_one(prod); fmpz_cmp(prod, bound) < 0; fmpz_mul_ui(prod, prod, p), p = n_nextprime(p, 0))
+ {
+ nmod_init(&nmod, p);
+ nmod_sparse_mat_init(&A_mod[np], A->r, A->r, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(&A_mod[np++], A);
+ }
+ fmpz_clear(prod);
+ fmpz_clear(bound);
+ *num_primes = np;
+ return flint_realloc(A_mod, np*sizeof(*A_mod));
+}
+
+int
+_fmpq_mat_check_solution_fmpz_sparse_mat(const fmpq_mat_t X, const fmpz_sparse_mat_t A, const fmpz_mat_t B)
+{
+ slong i, j;
+ fmpz_mat_t Xclear, AXclear;
+ fmpz_t t;
+ fmpz * Xden;
+ int ok;
+
+ Xden = _fmpz_vec_init(X->c);
+ fmpz_mat_init(Xclear, X->r, X->c);
+ fmpz_mat_init(AXclear, X->r, X->c);
+ fmpz_init(t);
+
+ fmpq_mat_get_fmpz_mat_colwise(Xclear, Xden, X);
+ fmpz_sparse_mat_mul_mat(AXclear, A, Xclear);
+
+ ok = 1;
+ for (i = 0; i < B->r && ok; i++)
+ {
+ for (j = 0; j < B->c && ok; j++)
+ {
+ /* AXclear[i,j] / Xden[j] = B[i,j] */
+ fmpz_mul(t, fmpz_mat_entry(B, i, j), Xden + j);
+
+ if (!fmpz_equal(t, fmpz_mat_entry(AXclear, i, j)))
+ ok = 0;
+ }
+ }
+
+ _fmpz_vec_clear(Xden, X->c);
+ fmpz_mat_clear(Xclear);
+ fmpz_mat_clear(AXclear);
+ fmpz_clear(t);
+
+ return ok;
+}
+
+int _fmpz_sparse_mat_solve_dixon(fmpz_mat_t X, fmpz_t mod, const fmpz_sparse_mat_t A, const fmpz_mat_t B, int rat_sol)
+{
+ slong i, j, jcheck, num_primes;
+ mp_limb_t p;
+ fmpz_t N, D, bound, prod;
+ nmod_sparse_mat_struct *A_mod;
+ nmod_sparse_mat_t Ainv;
+ nmod_mat_t d_mod, y_mod, Ay_mod;
+ fmpz_mat_t d, y, Ay;
+ fmpq_mat_t Q;
+
+ if (A->r != A->c)
+ {
+ flint_printf("Exception (fmpz_sparse_mat_solve_dixon). Non-square system matrix.\n");
+ flint_abort();
+ }
+
+ if (A->r == 0 || A->c == 0 || B->c == 0) return 1;
+ if (fmpz_sparse_mat_is_zero(A)) return 0;
+
+ fmpz_init(N);
+ fmpz_init(D);
+ fmpz_sparse_mat_solve_bound(N, D, A, B);
+ p = find_good_prime_and_invert(Ainv, A, D);
+ if (p == 0)
+ {
+ fmpz_clear(N);
+ fmpz_clear(D);
+ return 0;
+ }
+
+ /* Get collection of primes ~ p and construct A mod each */
+ A_mod = get_mod_mats(&num_primes, A, p);
+
+ /* Get bound on modulus for Hensel lift */
+ fmpz_init(bound);
+ if (fmpz_cmpabs(N, D) < 0)
+ fmpz_mul(bound, D, D);
+ else
+ fmpz_mul(bound, N, N);
+ fmpz_mul_ui(bound, bound, UWORD(2)); /* signs */
+
+ fmpz_mat_init(d, A->r, B->c);
+ fmpz_mat_init(y, A->r, B->c);
+ fmpz_mat_init(Ay, A->r, B->c);
+ nmod_mat_init(d_mod, A->r, B->c, p);
+ nmod_mat_init(y_mod, A->r, B->c, p);
+ nmod_mat_init(Ay_mod, A->r, B->c, p);
+ if (rat_sol) fmpq_mat_init(Q, A->r, B->c);
+
+ /* Initialize X to y = A^-1 b mod p */
+ fmpz_init(prod);
+ fmpz_mat_set(d, B);
+ fmpz_mat_get_nmod_mat(d_mod, d);
+ nmod_sparse_mat_mul_mat(y_mod, Ainv, d_mod);
+ fmpz_mat_set_nmod_mat_unsigned(X, y_mod);
+ j = jcheck = 1;
+ for (fmpz_set_ui(mod, p); fmpz_cmp(mod, bound) <= 0; fmpz_mul_ui(mod, mod, p))
+ {
+ if (rat_sol && j == jcheck)
+ {
+ if (fmpq_mat_set_fmpz_mat_mod_fmpz(Q, X, mod) && _fmpq_mat_check_solution_fmpz_sparse_mat(Q, A, B))
+ break;
+ jcheck = (slong)(j*1.4) + 1; /* Set when to check next */
+
+ }
+ /* Construct Ay via CRT */
+ for (i = 0; i < num_primes; i++)
+ {
+ _nmod_mat_set_mod(y_mod, A_mod[i].mod.n);
+ _nmod_mat_set_mod(Ay_mod, A_mod[i].mod.n);
+ nmod_sparse_mat_mul_mat(Ay_mod, &A_mod[i], y_mod);
+ if (i == 0)
+ {
+ fmpz_mat_set_nmod_mat(Ay, Ay_mod);
+ fmpz_set_ui(prod, p);
+ }
+ else
+ {
+ fmpz_mat_CRT_ui(Ay, Ay, prod, Ay_mod, 1);
+ fmpz_mul_ui(prod, prod, A_mod[i].mod.n);
+ }
+ }
+ /* fmpz_mat_set_nmod_mat_unsigned(y, y_mod);
+ fmpz_mat_mul(Ay, A, y); */
+
+ /* Update d = (d - Ay) / p */
+ fmpz_mat_sub(d, d, Ay);
+ fmpz_mat_scalar_divexact_ui(d, d, p);
+ fmpz_mat_get_nmod_mat(d_mod, d);
+
+ /* Update x = x + (A^(-1) * d mod p) * p^i [= A^(-1) * b mod p^(i+1)] */
+ _nmod_mat_set_mod(y_mod, p);
+ nmod_sparse_mat_mul_mat(y_mod, Ainv, d_mod);
+ fmpz_mat_scalar_addmul_nmod_mat_fmpz(X, y_mod, mod);
+ }
+ if (rat_sol)
+ {
+ if (fmpz_cmp(mod, bound) > 0) fmpq_mat_set_fmpz_mat_mod_fmpz(Q, X, mod);
+ fmpq_mat_get_fmpz_mat_matwise(X, mod, Q);
+ fmpq_mat_clear(Q);
+ }
+
+ nmod_mat_clear(d_mod);
+ nmod_mat_clear(y_mod);
+ nmod_mat_clear(Ay_mod);
+ fmpz_mat_clear(d);
+ fmpz_mat_clear(y);
+ fmpz_mat_clear(Ay);
+
+ for (i = 0; i < num_primes; i++) nmod_sparse_mat_clear(&A_mod[i]);
+ flint_free(A_mod);
+
+ fmpz_clear(bound);
+ fmpz_clear(prod);
+
+ nmod_sparse_mat_clear(Ainv);
+ fmpz_clear(N);
+ fmpz_clear(D);
+ return 1;
+}
+
+int fmpz_sparse_mat_solve_dixon(fmpz_mat_t X, fmpz_t mod, const fmpz_sparse_mat_t A, const fmpz_mat_t B)
+{
+ return _fmpz_sparse_mat_solve_dixon(X, mod, A, B, 0);
+}
+
+int fmpz_sparse_mat_solve_dixon_den(fmpz_mat_t X, fmpz_t den, const fmpz_sparse_mat_t A, const fmpz_mat_t B)
+{
+ return _fmpz_sparse_mat_solve_dixon(X, den, A, B, 1);
+}
diff --git a/fmpz_sparse_mat/strong_echelon_form_mod.c b/fmpz_sparse_mat/strong_echelon_form_mod.c
new file mode 100644
index 0000000000..9c8bba8449
--- /dev/null
+++ b/fmpz_sparse_mat/strong_echelon_form_mod.c
@@ -0,0 +1,191 @@
+/*
+ Copyright (C) 2015 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+
+
+
+/* Multiply by unit to minimize leading term (to a factor of N) */
+static void scale_row(fmpz_sparse_mat_with_transpose_t MT, slong r, const fmpz_t mod)
+{
+ fmpz_t g, a, b, n;
+ if (!fmpz_is_one(LT(MT->M, r).val))
+ {
+ fmpz_init(g);
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_xgcd(g, a, b, LT(MT->M, r).val, mod);
+ if (!fmpz_is_one(g)) /* Need to lift a = (M[pr][pc]/g)^-1 mod N/g to a unit modulo N */
+ {
+ fmpz_divexact(b, mod, g);
+ fmpz_init_set(n, mod);
+ while (!fmpz_is_one(g))
+ {
+ fmpz_gcd(g, a, n);
+ fmpz_divexact(n, n, g);
+ }
+ fmpz_addmul(a, n, b);
+ fmpz_mod(a, a, mod);
+ fmpz_clear(n);
+ }
+ MT_FIX(MT, r,
+ fmpz_sparse_vec_scalar_mul_fmpz(&MT->M->rows[r], &MT->M->rows[r], a);
+ fmpz_sparse_vec_scalar_mod_fmpz(&MT->M->rows[r], &MT->M->rows[r], mod);
+ );
+ fmpz_clear(g);
+ fmpz_clear(a);
+ fmpz_clear(b);
+ }
+}
+
+slong
+fmpz_sparse_mat_strong_echelon_form_mod(fmpz_sparse_mat_t M, const fmpz_t mod)
+{
+ slong i, r, c, pr, pc, rank, remr, nzrows;
+ slong *P; /* Row permutation */
+ slong *Pr; /* Map from column to associated pivot row */
+ slong *irows; /* Set of rows incident on a given column */
+ slong *zrows; /* Set of empty rows */
+ slong nnp; /* Number of such rows which are not pivots */
+ slong npp; /* Number of such rows which are not pivots */
+ fmpz_sparse_mat_with_transpose_t MT;
+ hashmap_struct *hcol; /* Virtual L^t and one of its rows */
+ fmpz_t q, zero;
+ fmpz_sparse_vec_t zero_vec;
+
+ if (fmpz_sparse_mat_is_zero(M)) return 0;
+ fmpz_init(zero);
+ fmpz_init(q);
+
+ /* Final object must have at least as many rows as columns */
+ fmpz_sparse_vec_init(zero_vec);
+ while (M->r < M->c) fmpz_sparse_mat_append_row(M, zero_vec);
+
+ /* Need extra row to deal with final elimination */
+ fmpz_sparse_mat_append_row(M, zero_vec);
+
+ /* Initialize data structure to hold copy of incident rows of a given column */
+ irows = flint_malloc(M->r*sizeof(*irows));
+ zrows = flint_malloc(M->r*sizeof(*zrows));
+
+ fmpz_sparse_mat_scalar_mod_fmpz(M, M, mod);
+
+ /* Set up permutation */
+ P = flint_malloc(M->r*sizeof(*P));
+ remr = M->r, nzrows = 0;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz) P[r] = --remr, zrows[nzrows++] = r;
+ else P[r] = -1;
+ }
+
+ /* Set up pivot row mapping */
+ Pr = flint_malloc(M->c*sizeof(*Pr));
+ for (c = 0; c < M->c; ++c)
+ Pr[c] = -1;
+
+ /* Construct virtual transpose */
+ _fmpz_sparse_mat_with_transpose_init(MT, M);
+
+ for (pc = 0; pc < M->c; ++pc)
+ {
+ hcol = &MT->cols[pc];
+ if (!hcol->num) continue;
+ pr = -1, nnp = 0;
+
+ /* Find incident row pr which is not a previous pivot and has minimal leading term */
+ /* Make pi_0 ... pi_{nnp-1} the other incident rows which are not previous pivots */
+ for (i = 0; i < hcol->num; ++i)
+ {
+ r = hcol->keys[i];
+ if (P[r] >= 0) continue;
+ else if (pr >= 0 && fmpz_cmpabs(LT(M, r).val, LT(M, pr).val) >= 0)
+ irows[nnp++] = r;
+ else
+ {
+ if (pr >= 0) irows[nnp++] = pr;
+ pr = r;
+ }
+ }
+ if (pr == -1) continue; /* Cannot perform elimination on this column (yet) */
+ scale_row(MT, pr, mod);
+ Pr[pc] = pr, P[pr] = pc;
+
+ /* Eliminate non-pivot rows */
+ for (i = 0; i < nnp; ++i)
+ {
+ r = irows[i];
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext_mod(MT, pr, r, mod);
+ if (M->rows[r].nnz == 0) P[r] = --remr, zrows[nzrows++] = r;
+ }
+ }
+
+ /* Reduce upwards, and deal with non-unit leading terms */
+ for (pc = 0; pc < M->c; pc++)
+ {
+ if (Pr[pc] == -1) {P[zrows[--nzrows]] = pc; continue;} /* No pivot for this column */
+ hcol = &MT->cols[pc];
+ pr = Pr[pc];
+
+ /* Reduce previous pivot rows */
+ for (i = npp = 0; i < hcol->num; ++i)
+ if (hcol->keys[i] != pr)
+ irows[npp++] = hcol->keys[i];
+ for (i = 0; i < npp; ++i)
+ {
+ _fmpz_sparse_mat_with_transpose_gauss_elim_mod(MT, pr, irows[i], mod);
+ }
+ if (fmpz_is_one(LT(M, pr).val)) continue;
+
+ /* Obtain (possibly) new basis element by scalar multiplicition */
+ r = zrows[--nzrows];
+ fmpz_divexact(q, mod, LT(M, pr).val);
+ MT_FIX(MT, r,
+ fmpz_sparse_vec_scalar_mul_fmpz(&M->rows[r], &M->rows[pr], q);
+ fmpz_sparse_vec_scalar_mod_fmpz(&M->rows[r], &M->rows[r], mod);
+ );
+
+ while (!fmpz_sparse_vec_is_zero(&M->rows[r]))
+ {
+ c = M->rows[r].entries[0].ind;
+ /* If no previous pivot row exists, use this row */
+ if (Pr[c] == -1)
+ {
+ scale_row(MT, r, mod);
+ Pr[c] = r; P[r] = c;
+ break;
+ }
+
+ /* Otherwise, eliminate c using existing pivot */
+ _fmpz_sparse_mat_with_transpose_gauss_elim_ext_mod(MT, Pr[c], r, mod);
+ if (!fmpz_sparse_vec_is_zero(&M->rows[r]) && LT(M, r).ind == c) flint_abort();
+ }
+ /* If row fully eliminated, add back to empty stock */
+
+ if (fmpz_sparse_vec_is_zero(&M->rows[r])) nzrows++;
+
+ }
+ fmpz_sparse_mat_permute_rows(M, P);
+ fmpz_clear(zero);
+ fmpz_clear(q);
+ flint_free(irows);
+ flint_free(zrows);
+ fmpz_sparse_vec_clear(zero_vec);
+ M->r -= 1;
+ M->rows = realloc(M->rows, M->r*sizeof(*M->rows));
+ _fmpz_sparse_mat_with_transpose_clear(MT);
+ flint_free(P);
+ flint_free(Pr);
+ return rank;
+}
+
diff --git a/fmpz_sparse_mat/test/t-CRT_ui.c b/fmpz_sparse_mat/test/t-CRT_ui.c
new file mode 100644
index 0000000000..daf3662893
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-CRT_ui.c
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2007 William Hart and David Harvey
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+#include "nmod_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, prime_bits, num_primes, j, nreps = 1000;
+ mp_limb_t primes[1000];
+ nmod_t nmod;
+ fmpz_t mod;
+ nmod_sparse_mat_t Amod;
+ fmpz_sparse_mat_t A, B;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("CRT_ui....");
+ fflush(stdout);
+
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = n_randint(state, 500) + 2;
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+ prime_bits = 1 + n_randint(state, FLINT_BITS - 1);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_init_set_ui(mod, UWORD(1));
+ for (num_primes = 0; fmpz_bits(mod) <= bits + 1; num_primes++)
+ {
+ primes[num_primes] = n_nextprime((num_primes == 0) ? (UWORD(1) << prime_bits) : primes[num_primes - 1], 0);
+ nmod_init(&nmod, primes[num_primes]);
+ nmod_sparse_mat_init(Amod, r, c, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(Amod, A);
+ if (num_primes == 0)
+ fmpz_sparse_mat_set_nmod_sparse_mat(B, Amod);
+ else
+ fmpz_sparse_mat_CRT_ui(B, B, mod, Amod, 1);
+ fmpz_mul_ui(mod, mod, primes[num_primes]);
+ nmod_sparse_mat_clear(Amod);
+ }
+ if (!fmpz_sparse_mat_equal(B, A))
+ {
+ flint_printf("FAIL!\n");
+ flint_printf("primes: ");
+ for (j = 0; j < num_primes; j++)
+ flint_printf("%wu ", primes[j]);
+ flint_printf("\nA: \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("\nB: \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-CRT_ui_unsigned.c b/fmpz_sparse_mat/test/t-CRT_ui_unsigned.c
new file mode 100644
index 0000000000..66339a71b0
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-CRT_ui_unsigned.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2007 William Hart and David Harvey
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+#include "nmod_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, prime_bits, num_primes, j, nreps = 1000;
+ mp_limb_t primes[1000];
+ nmod_t nmod;
+ fmpz_t mod;
+ nmod_sparse_mat_t Amod;
+ fmpz_sparse_mat_t A, B;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("CRT_ui_unsigned....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = n_randint(state, 500) + 1;
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+ prime_bits = 1 + n_randint(state, FLINT_BITS - 1);
+
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+
+ fmpz_sparse_mat_randtest_unsigned(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_init_set_ui(mod, UWORD(1));
+ for (num_primes = 0; fmpz_bits(mod) <= bits; num_primes++)
+ {
+ primes[num_primes] = n_nextprime((num_primes == 0) ? (UWORD(1) << prime_bits) : primes[num_primes - 1], 0);
+ nmod_init(&nmod, primes[num_primes]);
+ nmod_sparse_mat_init(Amod, r, c, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(Amod, A);
+ if (num_primes == 0)
+ fmpz_sparse_mat_set_nmod_sparse_mat_unsigned(B, Amod);
+ else
+ fmpz_sparse_mat_CRT_ui(B, B, mod, Amod, 0);
+ fmpz_mul_ui(mod, mod, primes[num_primes]);
+ nmod_sparse_mat_clear(Amod);
+ }
+
+ if (!fmpz_sparse_mat_equal(B, A))
+ {
+ flint_printf("FAIL!\n");
+ flint_printf("primes: ");
+ for (j = 0; j < num_primes; j++)
+ flint_printf("%wu ", primes[j]);
+ flint_printf("\nA: \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("\nB: \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-add.c b/fmpz_sparse_mat/test/t-add.c
new file mode 100644
index 0000000000..4640382989
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-add.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c;
+ fmpz_sparse_mat_t A, B, C, D;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("add/sub....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 200);
+ c = n_randint(state, 200);
+
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+ fmpz_sparse_mat_init(C, r, c);
+ fmpz_sparse_mat_init(D, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, c, bits);
+
+ fmpz_sparse_mat_add(C, A, B);
+ fmpz_sparse_mat_sub(D, C, B);
+
+ if (!fmpz_sparse_mat_equal(D, A))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ fmpz_sparse_mat_clear(D);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-concat_horizontal.c b/fmpz_sparse_mat/test/t-concat_horizontal.c
new file mode 100644
index 0000000000..e57ad8973d
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-concat_horizontal.c
@@ -0,0 +1,102 @@
+/*
+
+ Copyright (C) 2015 Elena Sergeicheva
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int main(void)
+{
+ slong rep, bits, r, c1, c2, nreps = 100;
+ fmpz_sparse_mat_t A, B, C;
+ fmpz_sparse_mat_t window1, window2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("concat_horizontal....");
+ fflush(stdout);
+
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 20);
+ c1 = n_randint(state, 20);
+ c2 = n_randint(state, 20);
+ fmpz_sparse_mat_init(A, r, c1);
+ fmpz_sparse_mat_init(B, r, c2);
+ fmpz_sparse_mat_init(C, r, c1+c2);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c1, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, c2, bits);
+ fmpz_sparse_mat_randtest(C, state, 0, c1+c2, bits);
+
+ fmpz_sparse_mat_concat_horizontal(C, A, B);
+
+ fmpz_sparse_mat_window_init(window1, C, 0, 0, r, c1);
+ fmpz_sparse_mat_window_init(window2, C, 0, c1, r, c1+c2);
+
+ if (!fmpz_sparse_mat_equal(window1, A) || !fmpz_sparse_mat_equal(window2, B))
+ {
+ flint_printf("A = \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("B = \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("A | B = \n");
+ fmpz_sparse_mat_print_pretty(C);
+ flint_printf("window1 = \n");
+ fmpz_sparse_mat_print_pretty(window1);
+ flint_printf("window2 = \n");
+ fmpz_sparse_mat_print_pretty(window2);
+ flint_printf("FAIL: window 2 not equal\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_window_clear(window1);
+ fmpz_sparse_mat_window_clear(window2);
+
+ fmpz_sparse_mat_init(window1, r, c1);
+ fmpz_sparse_mat_init(window2, r, c2);
+ fmpz_sparse_mat_split_horizontal(window1, window2, C, c1);
+
+ if (!(fmpz_sparse_mat_equal(window1, A) && fmpz_sparse_mat_equal(window2, B)))
+ {
+ flint_printf("A = \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("B = \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("A | B = \n");
+ fmpz_sparse_mat_print_pretty(C);
+ flint_printf("window1 = \n");
+ fmpz_sparse_mat_print_pretty(window1);
+ flint_printf("window2 = \n");
+ fmpz_sparse_mat_print_pretty(window2);
+ flint_printf("FAIL: results not equal\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(window1);
+ fmpz_sparse_mat_clear(window2);
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ }
+
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-concat_vertical.c b/fmpz_sparse_mat/test/t-concat_vertical.c
new file mode 100644
index 0000000000..0bda166c21
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-concat_vertical.c
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int main(void)
+{
+ slong rep, bits, r1, r2, c, nreps = 100;
+ fmpz_sparse_mat_t A, B, C;
+ fmpz_sparse_mat_t window1, window2;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("concat_vertical....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r1 = n_randint(state, 100);
+ r2 = n_randint(state, 100);
+ c = n_randint(state, 100);
+ fmpz_sparse_mat_init(A, r1, c);
+ fmpz_sparse_mat_init(B, r2, c);
+ fmpz_sparse_mat_init(C, r1+r2, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(C, state, 0, c, bits);
+
+ fmpz_sparse_mat_concat_vertical(C, A, B);
+
+ fmpz_sparse_mat_window_init(window1, C, 0, 0, r1, c);
+ fmpz_sparse_mat_window_init(window2, C, r1, 0, r1+r2, c);
+
+ if (!(fmpz_sparse_mat_equal(window1, A) && fmpz_sparse_mat_equal(window2, B)))
+ {
+ flint_printf("A = \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("B = \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("A concat_vertical B = \n");
+ fmpz_sparse_mat_print_pretty(C);
+ flint_printf("FAIL: results not equal\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_window_clear(window1);
+ fmpz_sparse_mat_window_clear(window2);
+
+ fmpz_sparse_mat_init(window1, r1, c);
+ fmpz_sparse_mat_init(window2, r2, c);
+ fmpz_sparse_mat_split_vertical(window1, window2, C, r1);
+
+ if (!fmpz_sparse_mat_equal(window1, A) || !fmpz_sparse_mat_equal(window2, B))
+ {
+ flint_printf("A = \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("B = \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("A concat_vertical B = \n");
+ fmpz_sparse_mat_print_pretty(C);
+ flint_printf("FAIL: results not equal\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(window1);
+ fmpz_sparse_mat_clear(window2);
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ }
+
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-construct.c b/fmpz_sparse_mat/test/t-construct.c
new file mode 100644
index 0000000000..ba9014abb9
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-construct.c
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c, i, j, k, nnz;
+ fmpz_sparse_mat_t A, B, C;
+ slong *rows;
+ slong *cols;
+ fmpz *vals;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("construction from entries....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ r = 1; /*n_randint(state, 10);*/
+ c = n_randint(state, 10);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+ fmpz_sparse_mat_init(C, 0, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, c, bits);
+ nnz = 0;
+ for (i = 0; i < r; ++i) nnz += A->rows[i].nnz;
+
+ /* Construct B from entries of A */
+ rows = flint_malloc(nnz * sizeof(*rows));
+ cols = flint_malloc(nnz * sizeof(*cols));
+ vals = _fmpz_vec_init(nnz);
+ for (i = k = 0; i < r; ++i)
+ {
+ for (j = 0; j < A->rows[i].nnz; ++j, ++k)
+ {
+ rows[k] = i;
+ cols[k] = A->rows[i].entries[j].ind;
+ fmpz_set(&vals[k], A->rows[i].entries[j].val);
+ }
+ }
+ fmpz_sparse_mat_from_entries(B, rows, cols, vals, nnz);
+
+ if (!fmpz_sparse_mat_equal(A, B))
+ {
+ flint_printf("FAIL: A != B\n");
+ flint_printf("A = ");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("B = ");
+ fmpz_sparse_mat_print_pretty(B);
+ abort();
+ }
+
+ for (i = 0; i < r; ++i) fmpz_sparse_mat_append_row(C, &A->rows[i]);
+ if (!fmpz_sparse_mat_equal(A, C))
+ {
+ flint_printf("FAIL: A != C\n");
+ flint_printf("A = ");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("C = ");
+ fmpz_sparse_mat_print_pretty(C);
+ abort();
+ }
+ flint_free(rows);
+ flint_free(cols);
+ _fmpz_vec_clear(vals, nnz);
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-content.c b/fmpz_sparse_mat/test/t-content.c
new file mode 100644
index 0000000000..7f34d90a9c
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-content.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2015 Dharak Kharod
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+#include "long_extras.h"
+
+
+int main()
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, nreps = 100;
+ fmpz_sparse_mat_t A,B;
+ fmpz_t scalar, gcd_mat, temp;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("fmpz_sparse_mat_content....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; ++rep)
+ {
+ r = n_randint(state, 50);
+ c = n_randint(state, 50);
+ min_nnz = 0;
+ max_nnz = c;
+ do bits = n_randint(state, 256);
+ while (bits <= UWORD(1));
+
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+
+ fmpz_init(scalar);
+ fmpz_init(gcd_mat);
+ fmpz_init(temp);
+
+ fmpz_sparse_mat_randtest(B, state, min_nnz, max_nnz, bits);
+
+ fmpz_sparse_mat_content(gcd_mat, B);
+
+ if (r == 0 || c == 0)
+ {
+ if (!fmpz_is_zero(gcd_mat))
+ {
+ flint_printf("FAIL!\n");
+ abort();
+ }
+ } else {
+ fmpz_randtest_not_zero(scalar, state, 50);
+
+ fmpz_sparse_mat_scalar_mul_fmpz(A, B, scalar);
+
+ fmpz_sparse_mat_content(temp, A);
+
+ fmpz_mul(gcd_mat, gcd_mat, scalar);
+
+ if (fmpz_cmpabs(gcd_mat, temp) != 0)
+ {
+ flint_printf("FAIL!\n");
+ abort();
+ }
+ }
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+
+ fmpz_clear(scalar);
+ fmpz_clear(temp);
+ fmpz_clear(gcd_mat);
+ }
+
+
+ FLINT_TEST_CLEANUP(state);
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-dense.c b/fmpz_sparse_mat/test/t-dense.c
new file mode 100644
index 0000000000..5a70031e4b
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-dense.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c;
+ fmpz_sparse_mat_t A, B;
+ fmpz_mat_t C, D;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("conversion to/from dense matrix....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ r = n_randint(state, 100);
+ c = n_randint(state, 100);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+ fmpz_mat_init(C, r, c);
+ fmpz_mat_init(D, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_to_dense(C, A);
+ fmpz_sparse_mat_from_dense(B, C);
+
+ if (!fmpz_sparse_mat_equal(A, B))
+ {
+ flint_printf("FAIL: A != B\n");
+ abort();
+ }
+
+ fmpz_mat_randtest(C, state, bits);
+ fmpz_sparse_mat_from_dense(A, C);
+ fmpz_sparse_mat_to_dense(D, A);
+
+ if (!fmpz_mat_equal(C, D))
+ {
+ flint_printf("FAIL: C != D\n");
+ abort();
+ }
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_mat_clear(C);
+ fmpz_mat_clear(D);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det.c b/fmpz_sparse_mat/test/t-det.c
new file mode 100644
index 0000000000..12f99015b2
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det.c
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ slong rep, bits, r, nreps = 100, nzero = 0;
+ fmpz_t det, d;
+ fmpz_mat_t dA;
+ fmpz_sparse_mat_t A;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ r = n_randint(state, 75);
+ bits = 1 + n_randint(state, 100);
+
+ fmpz_sparse_mat_init(A, r, r);
+
+ fmpz_init(det);
+ fmpz_init(d);
+
+ if (r == 0)
+ fmpz_one(d);
+ else
+ {
+ fmpz_mat_init(dA, r, r);
+ if (r > 1 && n_randint(state, 2) == 0)
+ {
+ fmpz_zero(d); nzero++;
+ fmpz_mat_randrank(dA, state, 1+n_randint(state, r - 1), bits);
+ }
+ else
+ {
+ fmpz_randtest(d, state, 30);
+ fmpz_mat_randdet(dA, state, d);
+ }
+ fmpz_mat_randops(dA, state, n_randint(state, FLINT_MAX(r/4, 1)*r + 1));
+ fmpz_sparse_mat_from_dense(A, dA);
+ fmpz_mat_clear(dA);
+ }
+
+ fmpz_sparse_mat_det(det, A);
+
+ if (!fmpz_equal(det, d))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("wrong determinant!\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("expected: "), fmpz_print(d), flint_printf("\n");
+ flint_printf("ncomputed: "), fmpz_print(det), flint_printf("\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_clear(det);
+ fmpz_clear(d);
+ }
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det_bareiss.c b/fmpz_sparse_mat/test/t-det_bareiss.c
new file mode 100644
index 0000000000..7c65540670
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det_bareiss.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ slong rep, bits, r, nreps = 1000;
+ fmpz_t det, ddet;
+ fmpz_mat_t dA;
+ fmpz_sparse_mat_t A;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det_bareiss....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 1+n_randint(state,200);
+ r = n_randint(state, 10);
+
+ fmpz_mat_init(dA, r, r);
+ fmpz_sparse_mat_init(A, r, r);
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+
+ fmpz_init(det);
+ fmpz_init(ddet);
+
+ fmpz_sparse_mat_det(det, A);
+ fmpz_sparse_mat_to_dense(dA, A);
+ fmpz_mat_det(ddet, dA);
+ if (!fmpz_equal(det, ddet))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("Incorrect determinant!\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("found det: "), fmpz_print(det), flint_printf("\n");
+ flint_printf("right det: "), fmpz_print(ddet), flint_printf("\n");
+ abort();
+ }
+
+ fmpz_clear(det);
+ fmpz_clear(ddet);
+ fmpz_mat_clear(dA);
+ fmpz_sparse_mat_clear(A);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det_bound.c b/fmpz_sparse_mat/test/t-det_bound.c
new file mode 100644
index 0000000000..b0a6e08c61
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det_bound.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ fmpz_sparse_mat_t A;
+ slong i, m;
+ fmpz_t det, bound;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det_bound....");
+ fflush(stdout);
+
+ for (i = 0; i < 1000 * flint_test_multiplier(); i++)
+ {
+ m = n_randint(state, 10);
+
+ fmpz_sparse_mat_init(A, m, m);
+
+ fmpz_init(det);
+ fmpz_init(bound);
+
+ fmpz_sparse_mat_randtest(A, state, 0, m, 1+n_randint(state,200));
+
+ fmpz_sparse_mat_det(det, A);
+ fmpz_sparse_mat_det_bound(bound, A);
+
+ if (fmpz_cmp(det, bound) > 0)
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("bound too small!\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("det: "), fmpz_print(det), flint_printf("\n");
+ flint_printf("bound: "), fmpz_print(bound), flint_printf("\n");
+ abort();
+ }
+
+ fmpz_clear(det);
+ fmpz_clear(bound);
+ fmpz_sparse_mat_clear(A);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det_divisor.c b/fmpz_sparse_mat/test/t-det_divisor.c
new file mode 100644
index 0000000000..ee4873a7a7
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det_divisor.c
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ slong rep, bits, r, nreps = 1000;
+ fmpz_t det, d;
+ fmpz_sparse_mat_t A;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det_divisor....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ r = n_randint(state, 15);
+ bits = 1 + n_randint(state, 100);
+
+ fmpz_init(det);
+ fmpz_init(d);
+ fmpz_sparse_mat_init(A, r, r);
+
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+
+ fmpz_sparse_mat_det_divisor(d, A);
+ fmpz_sparse_mat_det_bareiss(det, A);
+
+ if ((fmpz_is_zero(det) || fmpz_is_zero(d)) && !fmpz_equal(det, d))
+ {
+ flint_printf("FAIL: found divisor of matrix with det 0\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ abort();
+ }
+ else if (!fmpz_divisible(det, d))
+ {
+ flint_printf("FAIL:\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("det: "); fmpz_print(det); flint_printf("\n");
+ flint_printf("d: "); fmpz_print(d); flint_printf("\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_clear(det);
+ fmpz_clear(d);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det_modular.c b/fmpz_sparse_mat/test/t-det_modular.c
new file mode 100644
index 0000000000..de65ef2814
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det_modular.c
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ slong rep, bits, r, nreps = 1000;
+ fmpz_t det, d;
+ fmpz_sparse_mat_t A;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det_modular....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ int proved = n_randlimb(state) % 2;
+ bits = 1+n_randint(state,200);
+ r = n_randint(state, 20);
+
+ fmpz_sparse_mat_init(A, r, r);
+
+ fmpz_init(det);
+ fmpz_init(d);
+
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+
+ fmpz_sparse_mat_det_bareiss(d, A);
+ fmpz_sparse_mat_det_modular(det, A, proved);
+
+ if (!fmpz_equal(d, det))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("different determinants!\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("det_bareiss: "), fmpz_print(d), flint_printf("\n");
+ flint_printf("det_modular: "), fmpz_print(det), flint_printf("\n");
+ abort();
+ }
+
+ fmpz_clear(d);
+ fmpz_clear(det);
+ fmpz_sparse_mat_clear(A);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-det_modular_accelerated.c b/fmpz_sparse_mat/test/t-det_modular_accelerated.c
new file mode 100644
index 0000000000..23615aa08c
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-det_modular_accelerated.c
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+
+int
+main(void)
+{
+ slong rep, bits, r, nreps = 1000;
+ fmpz_t det, d;
+ fmpz_sparse_mat_t A;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("det_modular_accelerated....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ int proved = n_randlimb(state) % 2;
+ bits = 1+n_randint(state,200);
+ r = n_randint(state, 20);
+
+ fmpz_sparse_mat_init(A, r, r);
+
+ fmpz_init(det);
+ fmpz_init(d);
+
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+
+ fmpz_sparse_mat_det_bareiss(d, A);
+ fmpz_sparse_mat_det_modular_accelerated(det, A, proved);
+
+ if (!fmpz_equal(d, det))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("different determinants!\n");
+ fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("det_bareiss: "), fmpz_print(d), flint_printf("\n");
+ flint_printf("det_modular: "), fmpz_print(det), flint_printf("\n");
+ abort();
+ }
+
+ fmpz_clear(d);
+ fmpz_clear(det);
+ fmpz_sparse_mat_clear(A);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-fflu.c b/fmpz_sparse_mat/test/t-fflu.c
new file mode 100644
index 0000000000..67775f7a9f
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-fflu.c
@@ -0,0 +1,145 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "perm.h"
+#include "flint.h"
+#include "fmpz.h"
+#include "fmpz_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, rk, nreps = 1000, i, j;
+ slong *P, *Q;
+ fmpz_t *val;
+ fmpz *D;
+ fmpz_mat_t dL, dU, dLU;
+ fmpz_sparse_mat_t L, U, M, LU;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("fflu....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 100; /*2 + n_randint(state, 10);*/
+ r = c = n_randint(state, 10);
+ /*c = n_randint(state, 20);*/
+ min_nnz = 0;
+ max_nnz = c;
+
+ D = _fmpz_vec_init(r);
+ P = flint_malloc(r*sizeof(*P));
+ Q = flint_malloc(c*sizeof(*Q));
+ fmpz_sparse_mat_init(M, r, c);
+ fmpz_sparse_mat_init(L, r, c);
+ fmpz_sparse_mat_init(U, r, c);
+ fmpz_sparse_mat_init(LU, r, c);
+ fmpz_sparse_mat_randtest(M, state, min_nnz, max_nnz, bits);
+ rk = fmpz_sparse_mat_fflu(D, P, Q, L, U, M);
+
+ /* Check that L is lower triangular (with ones on diagonal up to rank) */
+ for (i = 0; i < r; ++i)
+ {
+ val = fmpz_sparse_vec_at(&L->rows[i], i);
+ if (i < rk && (val == NULL || !fmpz_is_one(*val)))
+ {
+ flint_printf("FAIL: L does not have unit diagonal up to the rank\n");
+ }
+ for (j = 0; j < L->rows[i].nnz; ++j)
+ {
+ if (L->rows[i].entries[j].ind > i)
+ {
+ flint_printf("FAIL: L not lower triangular\n");
+ abort();
+ }
+ if (L->rows[i].entries[j].ind >= rk)
+ {
+ flint_printf("FAIL: L not trivial past the rank\n");
+ flint_printf("rank = %wd\n", rk);
+ flint_printf("L = ");
+ fmpz_sparse_mat_print_pretty(L);
+ abort();
+ }
+ }
+ }
+ /* Check that U is upper triangular (with nonzero diagonal up to rank) */
+ for (i = 0; i < r; ++i)
+ {
+ val = fmpz_sparse_vec_at(&U->rows[i], i);
+ if (i < rk && (val == NULL || fmpz_is_zero(*val)))
+ {
+ flint_printf("FAIL: U does not have nonzero diagonal\n");
+ abort();
+ }
+ if (i >= rk && U->rows[i].nnz != UWORD(0))
+ {
+ flint_printf("FAIL: U not trivial past the rank\n");
+ abort();
+ }
+ for (j = 0; j < U->rows[i].nnz; ++j)
+ {
+ if (U->rows[i].entries[j].ind < i)
+ {
+ flint_printf("FAIL: U not upper triangular\n");
+ abort();
+ }
+ }
+ }
+
+
+ fmpz_mat_init(dL, r, rk);
+ fmpz_mat_init(dU, rk, c);
+ fmpz_mat_init(dLU, r, c);
+
+ /* Verify that PDMQ = LU */
+ fmpz_sparse_mat_mul_diag_fmpz(M, M, D);
+ fmpz_sparse_mat_permute_rows(M, P);
+ fmpz_sparse_mat_permute_cols(M, Q);
+ fmpz_sparse_mat_to_dense(dL, L);
+ fmpz_sparse_mat_to_dense(dU, U);
+ fmpz_mat_mul(dLU, dL, dU);
+ fmpz_sparse_mat_from_dense(LU, dLU);
+ if (!fmpz_sparse_mat_equal(M, LU))
+ {
+ flint_printf("FAIL: PDMQ != LU\n");
+ flint_printf("PDMQ=");
+ fmpz_sparse_mat_print_pretty(M);
+ flint_printf("LU=");
+ fmpz_sparse_mat_print_pretty(LU);
+ flint_printf("diff = ");
+ fmpz_sparse_mat_sub(LU, LU, M);
+ fmpz_sparse_mat_print_pretty(LU);
+ abort();
+ }
+
+ _fmpz_vec_clear(D, r);
+ flint_free(P);
+ flint_free(Q);
+ fmpz_sparse_mat_clear(M);
+ fmpz_sparse_mat_clear(U);
+ fmpz_sparse_mat_clear(L);
+ fmpz_sparse_mat_clear(LU);
+ fmpz_mat_clear(dL);
+ fmpz_mat_clear(dU);
+ fmpz_mat_clear(dLU);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-hnf_classical.c b/fmpz_sparse_mat/test/t-hnf_classical.c
new file mode 100644
index 0000000000..481fb7d7fa
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-hnf_classical.c
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, rk, nreps = 1000;
+ fmpz_mat_t dA, dH;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("hnf_classical....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 2 + n_randint(state, 100);
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+
+ fmpz_mat_init(dA, r, c);
+ fmpz_mat_init(dH, r, c);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(H, r, c);
+ fmpz_sparse_mat_init(H2, r, c);
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+ fmpz_sparse_mat_set(H, A);
+ rk = fmpz_sparse_mat_hnf_classical(H);
+ fmpz_sparse_mat_to_dense(dA, A);
+ fmpz_mat_hnf_classical(dH, dA);
+ fmpz_sparse_mat_from_dense(H2, dH);
+
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL: matrix not in hnf!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ flint_printf("Obtained: \n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ flint_printf("Should be: \n");
+ fmpz_sparse_mat_print_pretty(H2); flint_printf("\n\n");
+ flint_printf("Found rank %wd\n", rk);
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(H2);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(dA);
+ fmpz_mat_clear(dH);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-hnf_minors.c b/fmpz_sparse_mat/test/t-hnf_minors.c
new file mode 100644
index 0000000000..64d26e8d71
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-hnf_minors.c
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, rk, nreps = 1000;
+ fmpz_mat_t dA, dH;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("hnf_minors....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 2 + n_randint(state, 100);
+ r = n_randint(state, 20);
+ c = n_randint(state, 20);
+ min_nnz = 0;
+ max_nnz = c;
+
+ fmpz_mat_init(dA, r, c);
+ fmpz_mat_init(dH, r, c);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(H, r, c);
+ fmpz_sparse_mat_init(H2, r, c);
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+ fmpz_sparse_mat_set(H, A);
+ rk = fmpz_sparse_mat_hnf_minors(H);
+ fmpz_sparse_mat_to_dense(dA, A);
+ fmpz_mat_hnf(dH, dA);
+ fmpz_sparse_mat_from_dense(H2, dH);
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL: matrix not in hnf!\n");
+ flint_printf("Starting: \n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ flint_printf("Obtained: \n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ flint_printf("Found rank %wd\n", rk);
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(H2);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(dA);
+ fmpz_mat_clear(dH);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-hnf_modular.c b/fmpz_sparse_mat/test/t-hnf_modular.c
new file mode 100644
index 0000000000..0fe0d9a5fe
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-hnf_modular.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, min_nnz, max_nnz, bits, nreps = 1000;
+ fmpz_t det;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("hnf_modular....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 1 + n_randint(state, 200);
+ r = n_randint(state, 20);
+ min_nnz = 0;
+ max_nnz = r;
+
+ fmpz_init(det);
+
+ fmpz_sparse_mat_init(A, r, r);
+ fmpz_sparse_mat_init(H, r, r);
+ fmpz_sparse_mat_init(H2, r, r);
+
+ do
+ {
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+ fmpz_sparse_mat_det(det, A);
+ } while (fmpz_is_zero(det));
+ fmpz_abs(det, det);
+ fmpz_mul_ui(det, det, 1 + n_randint(state, 10));
+
+ fmpz_sparse_mat_set(H, A);
+ fmpz_sparse_mat_hnf_modular(H, det);
+
+ fmpz_sparse_mat_set(H2, A);
+ fmpz_sparse_mat_hnf_classical(H2);
+
+ if (!fmpz_sparse_mat_is_in_hnf(H))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("matrix not in hnf!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ abort();
+ }
+
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("hnfs produced by different methods should be the same!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H2); flint_printf("\n\n");
+ fmpz_print(det); flint_printf("\n\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(H2);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(A);
+ fmpz_clear(det);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-hnf_modular_eldiv.c b/fmpz_sparse_mat/test/t-hnf_modular_eldiv.c
new file mode 100644
index 0000000000..ffdc3f9320
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-hnf_modular_eldiv.c
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2015 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+
+/* FIX ME */
+int
+main(void)
+{
+ slong rep, r, min_nnz, max_nnz, bits, nreps = 100;
+ fmpz_t det;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("hnf_modular_eldiv....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 1 + n_randint(state, 100);
+ r = n_randint(state, 20);
+ min_nnz = 0;
+ max_nnz = r;
+
+ fmpz_init(det);
+
+ fmpz_sparse_mat_init(A, r, r);
+ fmpz_sparse_mat_init(H, r, r);
+ fmpz_sparse_mat_init(H2, r, r);
+
+ do
+ {
+ fmpz_sparse_mat_randtest_unsigned(A, state, min_nnz, max_nnz, bits);
+ fmpz_sparse_mat_det(det, A);
+ } while (fmpz_is_zero(det));
+ fmpz_abs(det, det);
+ /*fmpz_mul_ui(det, det, 1 + n_randint(state, 10));*/
+
+ fmpz_sparse_mat_set(H, A);
+ fmpz_sparse_mat_hnf_modular_eldiv(H, det);
+ fmpz_sparse_mat_set(H2, A);
+ fmpz_sparse_mat_hnf_minors(H2);
+
+ if (!fmpz_sparse_mat_is_in_hnf(H))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("matrix not in hnf!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ flint_printf("determinant:"); fmpz_print(det);
+ flint_printf("\n\n");
+ abort();
+ }
+
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("hnfs produced by different methods should be the same!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H2); flint_printf("\n\n");
+ fmpz_print(det); flint_printf("\n\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(H2);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(A);
+ fmpz_clear(det);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-hnf_xgcd.c b/fmpz_sparse_mat/test/t-hnf_xgcd.c
new file mode 100644
index 0000000000..f518250af9
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-hnf_xgcd.c
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2014 Alex J. Best
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, rk, nreps = 1000;
+ fmpz_mat_t dA, dH;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("hnf_xgcd....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 2 + n_randint(state, 100);
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+
+ fmpz_mat_init(dA, r, c);
+ fmpz_mat_init(dH, r, c);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(H, r, c);
+ fmpz_sparse_mat_init(H2, r, c);
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_sparse_mat_set(H, A);
+ rk = fmpz_sparse_mat_hnf_xgcd(H);
+ fmpz_sparse_mat_to_dense(dA, A);
+ fmpz_mat_hnf_xgcd(dH, dA);
+ fmpz_sparse_mat_from_dense(H2, dH);
+
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL: matrix not in hnf!\n");
+ fmpz_sparse_mat_print_pretty(A); flint_printf("\n\n");
+ flint_printf("Obtained: \n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ flint_printf("Should be: \n");
+ fmpz_sparse_mat_print_pretty(H2); flint_printf("\n\n");
+ flint_printf("Found rank %wd\n", rk);
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(H2);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(dA);
+ fmpz_mat_clear(dH);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-howell_form_mod.c b/fmpz_sparse_mat/test/t-howell_form_mod.c
new file mode 100644
index 0000000000..d346bfacca
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-howell_form_mod.c
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2015 Tommy Hofmann
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, rk, rk2, nreps = 10;
+ fmpz_t mod;
+ fmpz_mat_t dH;
+ fmpz_sparse_mat_t A, H, H2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("howell_form....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = 64 + n_randint(state, 70);
+ c = n_randint(state, 10);
+ r = c + n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+
+ fmpz_mat_init(dH, r, c);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(H, r, c);
+ fmpz_sparse_mat_init(H2, r, c);
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_init(mod);
+ do fmpz_randtest_not_zero(mod, state, bits);
+ while (fmpz_fits_si(mod));
+ fmpz_sparse_mat_set(H, A);
+ fmpz_sparse_mat_to_dense(dH, A);
+ rk = fmpz_sparse_mat_howell_form_mod(H, mod);
+ rk2 = fmpz_mat_howell_form_mod(dH, mod);
+ fmpz_sparse_mat_from_dense(H2, dH);
+ if (!fmpz_sparse_mat_equal(H, H2))
+ {
+ flint_printf("FAIL: Howell form not equal\n");
+ fmpz_sparse_mat_print_pretty(H); flint_printf("\n\n");
+ fmpz_sparse_mat_print_pretty(H2); flint_printf("\n\n");
+ flint_printf("Ranks: %wd, %wd\nModulus: ", rk, rk2);
+ fmpz_print(mod);
+ flint_printf("\n\n");
+ abort();
+ }
+
+ do fmpz_randtest_unsigned(mod, state, 10);
+ while (fmpz_is_zero(mod));
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(H);
+ fmpz_sparse_mat_clear(H2);
+ fmpz_mat_clear(dH);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
+
diff --git a/fmpz_sparse_mat/test/t-init_clear.c b/fmpz_sparse_mat/test/t-init_clear.c
new file mode 100644
index 0000000000..e5f3411131
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-init_clear.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c, i;
+ fmpz_sparse_mat_t A;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("init/clear....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 100; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 200);
+ c = n_randint(state, 200);
+ fmpz_sparse_mat_init(A, r, c);
+
+ if (!fmpz_sparse_mat_is_zero(A))
+ {
+ flint_printf("FAIL: A not zero!\n");
+ abort();
+ }
+ for (i = 0; i < r; i++)
+ {
+ if (!fmpz_sparse_vec_is_zero(&A->rows[i]))
+ {
+ flint_printf("FAIL: row %wd not zero!\n", i);
+ abort();
+ }
+ }
+
+ fmpz_sparse_mat_clear(A);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-mul.c b/fmpz_sparse_mat/test/t-mul.c
new file mode 100644
index 0000000000..e9d332695e
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-mul.c
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c, k;
+ fmpz_sparse_mat_t A;
+ fmpz *x, *y, *y2;
+ fmpz_mat_t B, X, Y, Y2;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("multiplication by vec/mat....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 70);
+ while (bits < UWORD(2));
+ r = 1 + n_randint(state, 20);
+ c = 1 + n_randint(state, 20);
+ k = 1 + n_randint(state, 20);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_mat_init(B, r, c);
+ fmpz_mat_init(X, c, k);
+ fmpz_mat_init(Y, r, k);
+ fmpz_mat_init(Y2, r, k);
+ x = _fmpz_vec_init(c);
+ y = _fmpz_vec_init(r);
+ y2 = _fmpz_vec_init(r);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_to_dense(B, A);
+ _fmpz_vec_randtest(x, state, c, bits);
+ fmpz_sparse_mat_mul_vec(y, A, x);
+ fmpz_mat_mul_vec(y2, B, x);
+
+ if (!_fmpz_vec_equal(y, y2, A->r))
+ {
+ flint_printf("FAIL: y != y2\n");
+ abort();
+ }
+
+ fmpz_mat_randtest(X, state, bits);
+ fmpz_sparse_mat_mul_mat(Y, A, X);
+ fmpz_mat_mul(Y2, B, X);
+
+ if (!fmpz_mat_equal(Y, Y2))
+ {
+ flint_printf("Fail: Y != Y2\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(B);
+ _fmpz_vec_clear(x, c);
+ _fmpz_vec_clear(y, r);
+ _fmpz_vec_clear(y2, r);
+ fmpz_mat_clear(X);
+ fmpz_mat_clear(Y);
+ fmpz_mat_clear(Y2);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-multi_CRT_ui.c b/fmpz_sparse_mat/test/t-multi_CRT_ui.c
new file mode 100644
index 0000000000..b51b8a5114
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-multi_CRT_ui.c
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2007 William Hart and David Harvey
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+#include "nmod_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, prime_bits, num_primes, j, nreps = 1000;
+ mp_limb_t primes[1000];
+ nmod_t nmod;
+ fmpz_t mod;
+ nmod_sparse_mat_struct Amod[1000];
+ fmpz_sparse_mat_t A, B;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("multi_CRT_ui....");
+ fflush(stdout);
+
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = n_randint(state, 500) + 2;
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+ prime_bits = 1 + n_randint(state, FLINT_BITS - 1);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_init_set_ui(mod, UWORD(1));
+ for (num_primes = 0; fmpz_bits(mod) <= bits + 1; num_primes++)
+ {
+ primes[num_primes] = n_nextprime((num_primes == 0) ? (UWORD(1) << prime_bits) : primes[num_primes - 1], 0);
+ nmod_init(&nmod, primes[num_primes]);
+ nmod_sparse_mat_init(&Amod[num_primes], r, c, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(&Amod[num_primes], A);
+ fmpz_mul_ui(mod, mod, primes[num_primes]);
+ }
+ fmpz_sparse_mat_multi_mod_ui(Amod, num_primes, A);
+ fmpz_sparse_mat_multi_CRT_ui(B, Amod, num_primes, 1);
+
+ if (!fmpz_sparse_mat_equal(B, A))
+ {
+ flint_printf("FAIL!\n");
+ flint_printf("primes: ");
+ for (j = 0; j < num_primes; j++)
+ flint_printf("%wu ", primes[j]);
+ flint_printf("\nA: \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("\nB: \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("\n");
+ abort();
+ }
+
+ for (j = 0; j < num_primes; j++)
+ nmod_sparse_mat_clear(&Amod[j]);
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-multi_CRT_ui_unsigned.c b/fmpz_sparse_mat/test/t-multi_CRT_ui_unsigned.c
new file mode 100644
index 0000000000..c83d63418d
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-multi_CRT_ui_unsigned.c
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2007 William Hart and David Harvey
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_sparse_mat.h"
+#include "nmod_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, r, c, min_nnz, max_nnz, bits, prime_bits, num_primes, j, nreps = 1000;
+ mp_limb_t primes[1000];
+ nmod_t nmod;
+ fmpz_t mod;
+ nmod_sparse_mat_struct Amod[1000];
+ fmpz_sparse_mat_t A, B;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("multi_CRT_ui_unsigned....");
+ fflush(stdout);
+
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ bits = n_randint(state, 500) + 2;
+ r = n_randint(state, 10);
+ c = n_randint(state, 10);
+ min_nnz = 0;
+ max_nnz = c;
+ prime_bits = 1 + n_randint(state, FLINT_BITS - 1);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+
+ fmpz_sparse_mat_randtest_unsigned(A, state, min_nnz, max_nnz, bits);
+
+ fmpz_init_set_ui(mod, UWORD(1));
+ for (num_primes = 0; fmpz_bits(mod) <= bits; num_primes++)
+ {
+ primes[num_primes] = n_nextprime((num_primes == 0) ? (UWORD(1) << prime_bits) : primes[num_primes - 1], 0);
+ nmod_init(&nmod, primes[num_primes]);
+ nmod_sparse_mat_init(&Amod[num_primes], r, c, nmod);
+ fmpz_sparse_mat_get_nmod_sparse_mat(&Amod[num_primes], A);
+ fmpz_mul_ui(mod, mod, primes[num_primes]);
+ }
+ fmpz_sparse_mat_multi_mod_ui(Amod, num_primes, A);
+ fmpz_sparse_mat_multi_CRT_ui(B, Amod, num_primes, 0);
+
+ if (!fmpz_sparse_mat_equal(B, A))
+ {
+ flint_printf("FAIL!\n");
+ flint_printf("primes: ");
+ for (j = 0; j < num_primes; j++)
+ flint_printf("%wu ", primes[j]);
+ flint_printf("\nA: \n");
+ fmpz_sparse_mat_print_pretty(A);
+ flint_printf("\nB: \n");
+ fmpz_sparse_mat_print_pretty(B);
+ flint_printf("\n");
+ abort();
+ }
+
+ for (j = 0; j < num_primes; j++)
+ nmod_sparse_mat_clear(&Amod[j]);
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-neg.c b/fmpz_sparse_mat/test/t-neg.c
new file mode 100644
index 0000000000..b4fd5c1873
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-neg.c
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c;
+ fmpz_sparse_mat_t A, B, C, D;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("neg....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 200);
+ c = n_randint(state, 200);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+ fmpz_sparse_mat_init(C, r, c);
+ fmpz_sparse_mat_init(D, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(C, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(D, state, 0, c, bits);
+
+ fmpz_sparse_mat_sub(C, A, B);
+ fmpz_sparse_mat_neg(B, B);
+ fmpz_sparse_mat_add(D, A, B);
+
+ if (!fmpz_sparse_mat_equal(C, D))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_neg(C, B);
+ fmpz_sparse_mat_neg(B, B);
+
+ if (!fmpz_sparse_mat_equal(C, B))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ fmpz_sparse_mat_clear(D);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-scalar_mul.c b/fmpz_sparse_mat/test/t-scalar_mul.c
new file mode 100644
index 0000000000..98b5daba12
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-scalar_mul.c
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c, nreps = 1000;
+ fmpz_t a, cc;
+ fmpz_sparse_mat_t A, B, C, D;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("scalar_mul....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 200);
+ c = n_randint(state, 200);
+ fmpz_init(a);
+ fmpz_init(cc);
+ fmpz_randtest(cc, state, bits);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, r, c);
+ fmpz_sparse_mat_init(C, r, c);
+ fmpz_sparse_mat_init(D, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+
+ fmpz_sparse_mat_scalar_mul_fmpz(B, A, a);
+ fmpz_one(cc);
+ fmpz_sub(cc, a, cc);
+ fmpz_sparse_mat_scalar_mul_fmpz(C, A, cc);
+
+ /* c*A - (c-1)*A == A */
+ fmpz_sparse_mat_sub(D, B, C);
+
+ if (!fmpz_sparse_mat_equal(A, D))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+
+ /* Aliasing */
+ fmpz_sparse_mat_scalar_mul_fmpz(C, A, a);
+ fmpz_sparse_mat_scalar_mul_fmpz(A, A, a);
+
+ if (!fmpz_sparse_mat_equal(A, C))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ fmpz_sparse_mat_clear(D);
+ fmpz_clear(a);
+ fmpz_clear(cc);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-solve_dixon.c b/fmpz_sparse_mat/test/t-solve_dixon.c
new file mode 100644
index 0000000000..7143abd9d7
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-solve_dixon.c
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c, nreps = 1000, num_bad_fail = 0;
+ fmpz_sparse_mat_t A;
+ fmpz_mat_t X, B, AX, AXm, Bm;
+ fmpz_t mod, det;
+ int success;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("solve_dixon....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ r = n_randint(state, 20);
+ c = n_randint(state, 20);
+
+ fmpz_sparse_mat_init(A, r, r);
+ fmpz_mat_init(B, r, c);
+ fmpz_mat_init(X, r, c);
+ fmpz_init(mod);
+
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+ fmpz_mat_randtest(B, state, 1+n_randint(state, 2)*bits);
+
+ success = fmpz_sparse_mat_solve_dixon(X, mod, A, B);
+ if (!success)
+ {
+ fmpz_init(det);
+ fmpz_sparse_mat_det(det, A);
+ if (!fmpz_is_zero(det))
+ {
+ num_bad_fail++;
+ }
+ fmpz_clear(det);
+ }
+ else
+ {
+ fmpz_mat_init(AX, r, c);
+ fmpz_mat_init(AXm, r, c);
+ fmpz_mat_init(Bm, r, c);
+
+ fmpz_sparse_mat_mul_mat(AX, A, X);
+ fmpz_mat_scalar_mod_fmpz(AXm, AX, mod);
+ fmpz_mat_scalar_mod_fmpz(Bm, B, mod);
+
+ if (!fmpz_mat_equal(AXm, Bm) || !success)
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("AX != B!\n");
+ flint_printf("A:\n"), fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("B:\n"), fmpz_mat_print_pretty(B), flint_printf("\n");
+ flint_printf("X:\n"), fmpz_mat_print_pretty(X), flint_printf("\n");
+ flint_printf("mod = "), fmpz_print(mod), flint_printf("\n");
+ flint_printf("AX:\n"), fmpz_mat_print_pretty(AX), flint_printf("\n");
+ abort();
+ }
+ fmpz_mat_clear(Bm);
+ fmpz_mat_clear(AX);
+ fmpz_mat_clear(AXm);
+ }
+
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(B);
+ fmpz_mat_clear(X);
+ fmpz_clear(mod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ if (num_bad_fail != 0) flint_printf("Number of unexpected failures: %wd\n", num_bad_fail);
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-solve_dixon_den.c b/fmpz_sparse_mat/test/t-solve_dixon_den.c
new file mode 100644
index 0000000000..64c842091a
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-solve_dixon_den.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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.h"
+#include "fmpz_vec.h"
+#include "fmpz_sparse_mat.h"
+#include "ulong_extras.h"
+
+/* TODO */
+int
+main(void)
+{
+ slong rep, bits, r, c, nreps = 1000, num_bad_fail = 0;
+ fmpz_sparse_mat_t A;
+ fmpz_mat_t X, B, AX;
+ fmpz_t den, det;
+ int success;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("solve_dixon_den....");
+ fflush(stdout);
+
+ for (rep = 0; rep < nreps; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ r = n_randint(state, 20);
+ c = n_randint(state, 20);
+
+ fmpz_sparse_mat_init(A, r, r);
+ fmpz_mat_init(B, r, c);
+ fmpz_mat_init(X, r, c);
+ fmpz_init(den);
+
+ fmpz_sparse_mat_randtest(A, state, 0, r, bits);
+ fmpz_mat_randtest(B, state, 1+n_randint(state, 2)*bits);
+
+ success = fmpz_sparse_mat_solve_dixon_den(X, den, A, B);
+ if (!success)
+ {
+ fmpz_init(det);
+ fmpz_sparse_mat_det(det, A);
+ if (!fmpz_is_zero(det))
+ {
+ num_bad_fail++;
+ }
+ fmpz_clear(det);
+ }
+ else
+ {
+ fmpz_mat_init(AX, r, c);
+ fmpz_sparse_mat_mul_mat(AX, A, X);
+ fmpz_mat_scalar_divexact_fmpz(AX, AX, den);
+
+ if (!fmpz_mat_equal(AX, B))
+ {
+ flint_printf("FAIL:\n");
+ flint_printf("AX != B!\n");
+ flint_printf("A:\n"), fmpz_sparse_mat_print_pretty(A), flint_printf("\n");
+ flint_printf("B:\n"), fmpz_mat_print_pretty(B), flint_printf("\n");
+ flint_printf("X:\n"), fmpz_mat_print_pretty(X), flint_printf("\n");
+ flint_printf("den = "), fmpz_print(den), flint_printf("\n");
+ flint_printf("AX:\n"), fmpz_mat_print_pretty(AX), flint_printf("\n");
+ abort();
+ }
+ fmpz_mat_clear(AX);
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_mat_clear(B);
+ fmpz_mat_clear(X);
+ fmpz_clear(den);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/test/t-transpose.c b/fmpz_sparse_mat/test/t-transpose.c
new file mode 100644
index 0000000000..814f2fbe0c
--- /dev/null
+++ b/fmpz_sparse_mat/test/t-transpose.c
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_mat.h"
+
+int
+main(void)
+{
+ slong rep, bits, r, c;
+ fmpz_sparse_mat_t A, B, C;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("transpose....");
+ fflush(stdout);
+
+ /* Rectangular transpose, same modulus */
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ r = n_randint(state, 20);
+ c = n_randint(state, 20);
+ fmpz_sparse_mat_init(A, r, c);
+ fmpz_sparse_mat_init(B, c, r);
+ fmpz_sparse_mat_init(C, r, c);
+
+ fmpz_sparse_mat_randtest(A, state, 0, c, bits);
+ fmpz_sparse_mat_randtest(B, state, 0, r, bits);
+ fmpz_sparse_mat_randtest(C, state, 0, c, bits);
+
+ fmpz_sparse_mat_transpose(B, A);
+ fmpz_sparse_mat_transpose(C, B);
+
+ if (!fmpz_sparse_mat_equal(C, A))
+ {
+ flint_printf("FAIL: C != A\n");
+ abort();
+ }
+
+ fmpz_sparse_mat_clear(A);
+ fmpz_sparse_mat_clear(B);
+ fmpz_sparse_mat_clear(C);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_mat/transpose.c b/fmpz_sparse_mat/transpose.c
new file mode 100644
index 0000000000..7800152ae3
--- /dev/null
+++ b/fmpz_sparse_mat/transpose.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void
+fmpz_sparse_mat_transpose(fmpz_sparse_mat_t B, const fmpz_sparse_mat_t A)
+{
+ slong r, c, i, j, *nnz;
+ fmpz_sparse_entry_struct *Ae, *Be;
+ FLINT_ASSERT(B->r == A->c);
+ FLINT_ASSERT(A->r == B->c);
+ nnz = flint_calloc(A->c, sizeof(*nnz));
+ /* Get number of nnzs in each column of A (thus each row of B) */
+ for (c = 0; c < A->c; ++c)
+ {
+ nnz[c] = 0;
+ }
+ for (r = 0; r < A->r; ++r)
+ {
+ for (i = 0; i < A->rows[r].nnz; ++i)
+ {
+ c = A->rows[r].entries[i].ind - A->c_off;
+ if (c >= A->c) break;
+ nnz[c]++;
+ }
+ }
+ /* Allocate space for nnz and reset counters */
+ for (c = 0; c < A->c; ++c)
+ {
+ _fmpz_sparse_vec_resize(&B->rows[c], nnz[c]);
+ nnz[c] = 0;
+ }
+ /* Put entries into transposed matrix */
+ for (r = 0; r < A->r; ++r)
+ {
+ for (i = 0; i < A->rows[r].nnz; ++i)
+ {
+ Ae = &A->rows[r].entries[i];
+ c = Ae->ind - A->c_off;
+ if (c >= A->c) break;
+ j = nnz[c]++;
+ Be = &B->rows[c].entries[j];
+ Be->ind = r;
+ fmpz_set(Be->val, Ae->val);
+ }
+ }
+ flint_free(nnz);
+ B->c_off = 0;
+}
diff --git a/fmpz_sparse_mat/window_init.c b/fmpz_sparse_mat/window_init.c
new file mode 100644
index 0000000000..9d1a50834d
--- /dev/null
+++ b/fmpz_sparse_mat/window_init.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+
+void fmpz_sparse_mat_window_init(fmpz_sparse_mat_t window, const fmpz_sparse_mat_t mat, slong r1, slong c1, slong r2, slong c2)
+{
+ slong i;
+ r2 = FLINT_MIN(r2, mat->r), r1 = FLINT_MIN(r1, r2);
+ c2 = FLINT_MIN(c2, mat->c), c1 = FLINT_MIN(c1, c2);
+ window->r = r2-r1;
+ window->c = c2-c1;
+ window->c_off = c1;
+ window->rows = flint_malloc(window->r*sizeof(*window->rows));
+ for (i = 0; i < window->r; ++i)
+ fmpz_sparse_vec_window_init(&window->rows[i], &mat->rows[i+r1], c1, c2);
+}
diff --git a/fmpz_sparse_mat/with_transpose_fix_support.c b/fmpz_sparse_mat/with_transpose_fix_support.c
new file mode 100644
index 0000000000..a5933fff2f
--- /dev/null
+++ b/fmpz_sparse_mat/with_transpose_fix_support.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "hashmap.h"
+#include "longlong.h"
+
+void _fmpz_sparse_mat_with_transpose_fix_support(fmpz_sparse_mat_with_transpose_t MT, slong r, slong *osupp, slong onnz)
+{
+ slong i = 0, j = 0, oc, nc, c;
+ fmpz_sparse_vec_struct *row = &MT->M->rows[r];
+
+ i = 0;
+ while (1)
+ {
+ oc = (i==onnz) ? MT->M->c : osupp[i];
+ nc = (j==row->nnz) ? MT->M->c : row->entries[j].ind;
+ c = FLINT_MIN(oc, nc);
+ if (c >= MT->M->c) break;
+ if (oc < nc) hashmap_rem(&MT->cols[c], r);
+ else hashmap_put(&MT->cols[c], r, &MT->M->rows[r].entries[j].val);
+ if (oc <= nc) ++i;
+ if (oc >= nc) ++j;
+ }
+}
+
diff --git a/fmpz_sparse_mat/with_transpose_init.c b/fmpz_sparse_mat/with_transpose_init.c
new file mode 100644
index 0000000000..f45781f7d2
--- /dev/null
+++ b/fmpz_sparse_mat/with_transpose_init.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_mat.h"
+#include "hashmap.h"
+#include "longlong.h"
+
+void _fmpz_sparse_mat_with_transpose_init(fmpz_sparse_mat_with_transpose_t MT, fmpz_sparse_mat_t M)
+{
+ slong r, c, j;
+ MT->M = M;
+
+ /* Construct virtual transpose */
+ MT->cols = flint_calloc(M->c, sizeof(*MT->cols));
+ for (r = 0; r < M->r; ++r)
+ for (j = 0; j < M->rows[r].nnz; ++j)
+ if (M->rows[r].entries[j].ind < M->c)
+ MT->cols[M->rows[r].entries[j].ind].num++;
+ for (c = 0; c < M->c; ++c)
+ hashmap_init(&MT->cols[c], MT->cols[c].num);
+ for (r = 0; r < M->r; ++r)
+ for (j = 0; j < M->rows[r].nnz; ++j)
+ if (M->rows[r].entries[j].ind < M->c)
+ hashmap_put(&MT->cols[M->rows[r].entries[j].ind], r, &M->rows[r].entries[j].val);
+}
diff --git a/fmpz_sparse_vec.h b/fmpz_sparse_vec.h
new file mode 100644
index 0000000000..d25568ff0f
--- /dev/null
+++ b/fmpz_sparse_vec.h
@@ -0,0 +1,446 @@
+/*
+ Copyright (C) 2010 William Hart
+ Copyright (C) 2010,2011 Fredrik Johansson
+ Copyright (C) 2014 Ashish Kedia
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifndef FMPZ_SPARSE_VEC_H
+#define FMPZ_SPARSE_VEC_H
+
+#ifdef FMPZ_SPARSE_VEC_INLINES_C
+#define FMPZ_SPARSE_VEC_INLINE FLINT_DLL
+#else
+#define FMPZ_SPARSE_VEC_INLINE static __inline__
+#endif
+
+#undef ulong
+#define ulong ulongxx /* interferes with system includes */
+#include
+#include
+#undef ulong
+#include
+#define ulong mp_limb_t
+
+#include "flint.h"
+#include "longlong.h"
+#include "ulong_extras.h"
+#include "nmod_sparse_vec.h"
+#include "fmpz_vec.h"
+#include "fmpz.h"
+#include "thread_support.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct {
+ slong ind;
+ fmpz_t val;
+} fmpz_sparse_entry_struct;
+
+typedef fmpz_sparse_entry_struct fmpz_sparse_entry_t[1];
+
+typedef struct {
+ fmpz_sparse_entry_struct *entries;
+ slong nnz;
+} fmpz_sparse_vec_struct;
+
+typedef fmpz_sparse_vec_struct fmpz_sparse_vec_t[1];
+
+FMPZ_SPARSE_VEC_INLINE
+int fmpz_sparse_entry_cmp(const void *va, const void *vb)
+{
+ const fmpz_sparse_entry_struct *a = va;
+ const fmpz_sparse_entry_struct *b = vb;
+ if (a->ind < b->ind) return -1;
+ if (b->ind < a->ind) return 1;
+ return 0;
+}
+
+/* Memory management */
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_init(fmpz_sparse_vec_t vec)
+{
+ memset(vec, 0, sizeof(*vec));
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_clear(fmpz_sparse_vec_t vec)
+{
+ slong i;
+ for (i = 0; i < vec->nnz; ++i)
+ fmpz_clear(vec->entries[i].val);
+ flint_free(vec->entries);
+ memset(vec, 0, sizeof(*vec));
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void _fmpz_sparse_vec_resize(fmpz_sparse_vec_t vec, slong nnz)
+{
+ slong i;
+ FLINT_ASSERT(nnz >= 0);
+ if (nnz == 0) fmpz_sparse_vec_clear(vec);
+ else if (nnz != vec->nnz)
+ {
+ for (i = nnz; i < vec->nnz; ++i)
+ fmpz_clear (vec->entries[i].val);
+ vec->entries = flint_realloc(vec->entries, nnz*sizeof(*vec->entries));
+ for (i = vec->nnz; i < nnz; ++i)
+ fmpz_init (vec->entries[i].val);
+ }
+ vec->nnz = nnz;
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_resize(fmpz_sparse_vec_t vec, slong cols)
+{
+ slong i;
+ FLINT_ASSERT(cols >= 0);
+ if (vec->nnz == 0) return;
+ for (i = 0; i < vec->nnz; ++i) {
+ if (vec->entries[i].ind >= cols)
+ {
+ break;
+ }
+ }
+ _fmpz_sparse_vec_resize(vec, i);
+}
+
+FMPZ_SPARSE_VEC_INLINE
+slong _fmpz_sparse_vec_support(slong **supp, fmpz_sparse_vec_t vec)
+{
+ slong i;
+ *supp = flint_malloc(vec->nnz*sizeof(**supp));
+ for (i = 0; i < vec->nnz; ++i) (*supp)[i] = vec->entries[i].ind;
+ return vec->nnz;
+}
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_swap(fmpz_sparse_vec_t vec1, fmpz_sparse_vec_t vec2)
+{
+ fmpz_sparse_vec_t tmp;
+ *tmp = *vec1, *vec1 = *vec2, *vec2 = *tmp;
+}
+
+FLINT_DLL
+slong fmpz_sparse_vec_max_bits(const fmpz_sparse_vec_t v);
+
+/* Vector indexing */
+FLINT_DLL
+fmpz_t * fmpz_sparse_vec_at(const fmpz_sparse_vec_t vec, slong i);
+
+/* One-time instantiation */
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_zero(fmpz_sparse_vec_t vec)
+{
+ fmpz_sparse_vec_clear(vec);
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_one(fmpz_sparse_vec_t vec, slong ind)
+{
+ _fmpz_sparse_vec_resize(vec, 1);
+ vec->entries[0].ind = ind;
+ fmpz_one(vec->entries[0].val);
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_set(fmpz_sparse_vec_t vec, const fmpz_sparse_vec_t src, slong ioff);
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_set_entry(fmpz_sparse_vec_t v, slong ind, const fmpz_t val)
+{
+ fmpz_t * oval = fmpz_sparse_vec_at(v, ind);
+ if (oval == NULL)
+ {
+ _fmpz_sparse_vec_resize(v, v->nnz+1);
+ v->entries[v->nnz-1].ind = ind;
+ fmpz_set(v->entries[v->nnz-1].val, val);
+ if (v->nnz >= 2 && v->entries[v->nnz-2].ind > ind)
+ qsort(v->entries, v->nnz, sizeof(*v->entries), fmpz_sparse_entry_cmp);
+ }
+ else fmpz_set(*oval, val);
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_from_entries(fmpz_sparse_vec_t vec, slong * inds, fmpz * vals, slong nnz);
+
+/* Vector comparison */
+FMPZ_SPARSE_VEC_INLINE
+int fmpz_sparse_vec_is_zero(const fmpz_sparse_vec_t vec)
+{
+ return vec->nnz == 0;
+}
+
+FLINT_DLL
+int fmpz_sparse_vec_equal(const fmpz_sparse_vec_t vec1, const fmpz_sparse_vec_t vec2, slong ioff);
+
+/* Convert from/to dense vector */
+FLINT_DLL
+void fmpz_sparse_vec_from_dense(fmpz_sparse_vec_t vec, const fmpz *src, slong len);
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_to_dense(fmpz *vec, const fmpz_sparse_vec_t src, slong len)
+{
+ slong i;
+ _fmpz_vec_zero(vec, len);
+ for (i = 0; i < src->nnz; ++i)
+ if (src->entries[i].ind < len)
+ fmpz_set(&vec[src->entries[i].ind], src->entries[i].val);
+}
+
+/* To/from modular vector(s) */
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_get_nmod_sparse_vec(nmod_sparse_vec_t dst, const fmpz_sparse_vec_t src, const nmod_t mod)
+{
+ slong i, ind;
+ if (src->nnz == 0)
+ nmod_sparse_vec_zero(dst);
+ else
+ {
+ dst->entries = flint_realloc(dst->entries, src->nnz*sizeof(*dst->entries));
+ for (i = ind = 0; i < src->nnz; ++i)
+ {
+ dst->entries[ind].ind = src->entries[i].ind;
+ dst->entries[ind].val = fmpz_fdiv_ui(src->entries[i].val, mod.n);
+ if (dst->entries[ind].val != UWORD(0)) ++ind;
+ }
+ if (ind == 0)
+ {
+ flint_free(dst->entries);
+ dst->entries = NULL;
+ }
+ else dst->entries = flint_realloc(dst->entries, ind*sizeof(*dst->entries));
+ dst->nnz = ind;
+ }
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_set_nmod_sparse_vec_unsigned(fmpz_sparse_vec_t dst, const nmod_sparse_vec_t src)
+{
+ slong i;
+ _fmpz_sparse_vec_resize(dst, src->nnz);
+ for (i = 0; i < src->nnz; ++i)
+ {
+ dst->entries[i].ind = src->entries[i].ind;
+ fmpz_set_ui(dst->entries[i].val, src->entries[i].val);
+ }
+}
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_set_nmod_sparse_vec(fmpz_sparse_vec_t dst, const nmod_sparse_vec_t src, const nmod_t mod)
+{
+ slong i;
+ _fmpz_sparse_vec_resize(dst, src->nnz);
+ for (i = 0; i < src->nnz; ++i)
+ {
+ dst->entries[i].ind = src->entries[i].ind;
+ fmpz_set_ui_smod(dst->entries[i].val, src->entries[i].val, mod.n);
+ }
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_multi_mod_ui_precomp(nmod_sparse_vec_struct * residues, slong nres, const fmpz_sparse_vec_t v,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp);
+
+FLINT_DLL
+void fmpz_sparse_vec_multi_mod_ui(nmod_sparse_vec_struct * residues, mp_srcptr primes, slong nres, const fmpz_sparse_vec_t v);
+
+FLINT_DLL
+void fmpz_sparse_vec_CRT_ui(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_t m1,
+ const nmod_sparse_vec_t v, nmod_t m2, mp_limb_t m1i_m2, int sign);
+
+FLINT_DLL
+void fmpz_sparse_vec_multi_CRT_ui_precomp(fmpz_sparse_vec_t v, nmod_sparse_vec_struct const * residues, slong nres,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp, int sign);
+
+FLINT_DLL
+void fmpz_sparse_vec_multi_CRT_ui(fmpz_sparse_vec_t v, nmod_sparse_vec_struct * const residues, mp_srcptr primes, slong nres, int sign);
+
+/* Windows and concatenation */
+FLINT_DLL
+void fmpz_sparse_vec_window_init(fmpz_sparse_vec_t window, const fmpz_sparse_vec_t vec, slong i1, slong i2);
+
+
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_window_clear(fmpz_sparse_vec_t window)
+{
+ memset(window, 0, sizeof(*window));
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_concat(fmpz_sparse_vec_t res, const fmpz_sparse_vec_t vec1, const fmpz_sparse_vec_t vec2, slong len1);
+
+FLINT_DLL
+void fmpz_sparse_vec_split(fmpz_sparse_vec_t res1, fmpz_sparse_vec_t res2, const fmpz_sparse_vec_t vec, slong ind);
+
+/* Vector permutation */
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_permute_inds(fmpz_sparse_vec_t vec, slong *P)
+{
+ slong i;
+ for (i = 0; i < vec->nnz; ++i) vec->entries[i].ind = P[vec->entries[i].ind];
+}
+
+/* Random vector generation */
+FLINT_DLL
+void fmpz_sparse_vec_randtest(fmpz_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, flint_bitcnt_t bits);
+
+FLINT_DLL
+void fmpz_sparse_vec_randtest_unsigned(fmpz_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, flint_bitcnt_t bits);
+
+/* Vector display */
+FLINT_DLL
+void fmpz_sparse_vec_print_pretty(const fmpz_sparse_vec_t vec, slong ioff, slong maxi);
+
+/* Vector operations */
+FMPZ_SPARSE_VEC_INLINE
+void fmpz_sparse_vec_neg(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u)
+{
+ slong i;
+ fmpz_sparse_vec_set(v, u, 0);
+ for (i = 0; i < v->nnz; ++i) fmpz_neg(v->entries[i].val, v->entries[i].val);
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_mul_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t c);
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_divexact_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t c);
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_mod_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t mod);
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_mods_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t mod);
+
+/* Utility macros used by binary vector operations */
+/* Compute total number of indices between two sparse vectors */
+FMPZ_SPARSE_VEC_INLINE
+slong _fmpz_sparse_vec_union_nnz(const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v)
+{
+ slong i, j, nnz = 0;
+ for (i = j = 0; i < u->nnz && j < v->nnz; ++nnz)
+ {
+ if (u->entries[i].ind == v->entries[j].ind) ++i, ++j;
+ else if (u->entries[i].ind < v->entries[j].ind) ++i;
+ else if (u->entries[i].ind > v->entries[j].ind) ++j;
+ }
+ nnz += u->nnz - i + v->nnz - j;
+ return nnz;
+}
+
+/* Utility macros used by binary vector operations */
+/* Compute total number of indices between two sparse vectors */
+FMPZ_SPARSE_VEC_INLINE
+slong _fmpz_sparse_vec_union_nnz_nmod(const fmpz_sparse_vec_t u, const nmod_sparse_vec_t v)
+{
+ slong i, j, nnz = 0;
+ for (i = j = 0; i < u->nnz && j < v->nnz; ++nnz)
+ {
+ if (u->entries[i].ind == v->entries[j].ind) ++i, ++j;
+ else if (u->entries[i].ind < v->entries[j].ind) ++i;
+ else if (u->entries[i].ind > v->entries[j].ind) ++j;
+ }
+ nnz += u->nnz - i + v->nnz - j;
+ return nnz;
+}
+
+/* Iterate through u and v in descending order, assigning sorted indices to w */
+/* Returns -1 if u and v are both exhausted,
+ 2 if we->ind = ue->ind == ve->ind
+ 1 if we->ind = ve->ind > ue->ind (or u exhausted),
+ 0 if we->ind = ue->ind > ve->ind (or v exhausted). */
+FMPZ_SPARSE_VEC_INLINE
+slong _fmpz_sparse_vector_merge_descend(fmpz_sparse_entry_struct **we,
+ fmpz_sparse_entry_struct **ue,
+ fmpz_sparse_entry_struct **ve,
+ const fmpz_sparse_vec_t u,
+ const fmpz_sparse_vec_t v)
+{
+ slong uind = (*ue==u->entries) ? -1 : (*ue-1)->ind;
+ slong vind = (*ve==v->entries) ? -1 : (*ve-1)->ind;
+ if (uind == -1 && vind == -1) return -1;
+ if (uind == vind) {--*ue, --*ve, --*we; (*we)->ind = uind; return 2;}
+ if (uind < vind) {--*ve, --*we; (*we)->ind = vind; return 1;}
+ --*ue, --*we; (*we)->ind = uind; return 0;
+}
+
+/* Iterate through u and v in descending order, assigning sorted indices to w */
+/* Returns -1 if u and v are both exhausted,
+ 2 if we->ind = ue->ind == ve->ind
+ 1 if we->ind = ve->ind > ue->ind (or u exhausted),
+ 0 if we->ind = ue->ind > ve->ind (or v exhausted). */
+FMPZ_SPARSE_VEC_INLINE
+slong _fmpz_sparse_vector_merge_descend_nmod(fmpz_sparse_entry_struct **we,
+ fmpz_sparse_entry_struct **ue,
+ nmod_sparse_entry_struct **ve,
+ const fmpz_sparse_vec_t u,
+ const nmod_sparse_vec_t v)
+{
+ slong uind = (*ue==u->entries) ? -1 : (*ue-1)->ind;
+ slong vind = (*ve==v->entries) ? -1 : (*ve-1)->ind;
+ if (uind == -1 && vind == -1) return -1;
+ if (uind == vind) {--*ue, --*ve, --*we; (*we)->ind = uind; return 2;}
+ if (uind < vind) {--*ve, --*we; (*we)->ind = vind; return 1;}
+ --*ue, --*we; (*we)->ind = uind; return 0;
+}
+
+/* Like resize, but removes entries from the front of the vector */
+FMPZ_SPARSE_VEC_INLINE
+void _fmpz_sparse_vector_shift_left (fmpz_sparse_vec_t v, slong amt)
+{
+ slong i;
+ if (amt == v->nnz) fmpz_sparse_vec_clear(v);
+ else if (amt > 0)
+ {
+ for (i = amt; i < v->nnz; ++i)
+ {
+ v->entries[i-amt].ind = v->entries[i].ind;
+ fmpz_set(v->entries[i - amt].val, v->entries[i].val);
+ }
+ _fmpz_sparse_vec_resize(v, v->nnz - amt);
+ }
+}
+
+FLINT_DLL
+void fmpz_sparse_vec_add(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v);
+
+FLINT_DLL
+void fmpz_sparse_vec_sub(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v);
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_addmul_fmpz(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, const fmpz_t c);
+
+FLINT_DLL
+void fmpz_sparse_vec_scalar_submul_fmpz(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, const fmpz_t c);
+
+FLINT_DLL
+void fmpz_sparse_vec_dot(fmpz_t ret, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v);
+
+FLINT_DLL
+void fmpz_sparse_vec_dot_dense(fmpz_t ret, const fmpz_sparse_vec_t u, const fmpz *v);
+
+/* Gaussian elimination operations */
+FLINT_DLL
+void fmpz_sparse_vec_gauss_elim_col(fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, slong col);
+
+FLINT_DLL
+void fmpz_sparse_vec_gauss_elim(fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v);
+
+FLINT_DLL
+void fmpz_sparse_vec_gauss_elim_ext(fmpz_sparse_vec_t u, fmpz_sparse_vec_t v);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/fmpz_sparse_vec/CRT_ui.c b/fmpz_sparse_vec/CRT_ui.c
new file mode 100644
index 0000000000..ed34f7616f
--- /dev/null
+++ b/fmpz_sparse_vec/CRT_ui.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "nmod_sparse_vec.h"
+#include "fmpz_sparse_vec.h"
+
+void
+fmpz_sparse_vec_CRT_ui(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_t m1,
+ const nmod_sparse_vec_t v, nmod_t m2, mp_limb_t m1i_m2, int sign)
+{
+ slong unnz = u->nnz, vnnz = v->nnz, wnnz, k;
+ fmpz_sparse_entry_struct *ue, *we;
+ nmod_sparse_entry_struct *ve;
+ fmpz_t m1m2, zero;
+ fmpz_init(m1m2);
+ fmpz_init_set_ui(zero, UWORD(0));
+ fmpz_mul_ui(m1m2, m1, m2.n);
+
+ wnnz = _fmpz_sparse_vec_union_nnz_nmod (u, v);
+ _fmpz_sparse_vec_resize(w, wnnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz, we = w->entries + wnnz;
+ while ((k = _fmpz_sparse_vector_merge_descend_nmod (&we, &ue, &ve, u, v)) >= 0)
+ {
+ _fmpz_CRT_ui_precomp(we->val, (k==1)?zero:ue->val, m1, (k==0)?UWORD(0):ve->val,
+ m2.n, m2.ninv, m1m2, m1i_m2, sign);
+ }
+ fmpz_clear(zero);
+ fmpz_clear(m1m2);
+}
+
diff --git a/fmpz_sparse_vec/add.c b/fmpz_sparse_vec/add.c
new file mode 100644
index 0000000000..82248c5091
--- /dev/null
+++ b/fmpz_sparse_vec/add.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include
+#include
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+
+void fmpz_sparse_vec_add(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v)
+{
+ slong unnz = u->nnz, vnnz = v->nnz, wnnz, k;
+ fmpz_sparse_entry_struct *ue, *ve, *we;
+
+ /* Check for simpler operations first */
+ if (vnnz == 0) fmpz_sparse_vec_set(w, u, 0);
+ else if (unnz == 0) fmpz_sparse_vec_set(w, v, 0);
+ else
+ {
+ wnnz = _fmpz_sparse_vec_union_nnz (u, v);
+ _fmpz_sparse_vec_resize(w, wnnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz, we = w->entries + wnnz;
+ while ((k = _fmpz_sparse_vector_merge_descend (&we, &ue, &ve, u, v)) >= 0)
+ {
+ switch(k)
+ {
+ case 0: fmpz_set(we->val, ue->val); break;
+ case 1: fmpz_set(we->val, ve->val); break;
+ default: fmpz_add(we->val, ue->val, ve->val);
+ if (fmpz_is_zero(we->val)) we++;
+ }
+ }
+ _fmpz_sparse_vector_shift_left (w, we - w->entries);
+ }
+}
diff --git a/fmpz_sparse_vec/at.c b/fmpz_sparse_vec/at.c
new file mode 100644
index 0000000000..78bc8443fe
--- /dev/null
+++ b/fmpz_sparse_vec/at.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+fmpz_t *fmpz_sparse_vec_at(const fmpz_sparse_vec_t vec, slong i)
+{
+ /* TODO: binary search */
+ slong j;
+ for (j = 0; j < vec->nnz; ++j)
+ if (vec->entries[j].ind==i) return &vec->entries[j].val;
+ return NULL;
+}
+
diff --git a/fmpz_sparse_vec/concat.c b/fmpz_sparse_vec/concat.c
new file mode 100644
index 0000000000..2ec111ad27
--- /dev/null
+++ b/fmpz_sparse_vec/concat.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_concat(fmpz_sparse_vec_t res, const fmpz_sparse_vec_t vec1, const fmpz_sparse_vec_t vec2, slong len1)
+{
+ slong i, nnz = vec1->nnz + vec2->nnz;
+ _fmpz_sparse_vec_resize (res, nnz);
+ for (i = 0; i < nnz; ++i)
+ {
+ fmpz_sparse_entry_struct *e = (i < vec1->nnz) ? &vec1->entries[i] : &vec2->entries[i-vec1->nnz];
+ res->entries[i].ind = e->ind + ((i < vec1->nnz) ? 0 : len1);
+ fmpz_set (res->entries[i].val, e->val);
+ }
+}
diff --git a/fmpz_sparse_vec/dot.c b/fmpz_sparse_vec/dot.c
new file mode 100644
index 0000000000..63750299c3
--- /dev/null
+++ b/fmpz_sparse_vec/dot.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_dot(fmpz_t ret, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v)
+{
+ slong i, j;
+ fmpz_zero(ret);
+ for (i = j = 0; i < u->nnz && j < v->nnz; )
+ {
+ if (u->entries[i].ind == v->entries[j].ind)
+ {
+ fmpz_addmul(ret, u->entries[i].val, v->entries[j].val);
+ ++i; ++j;
+ }
+ else if (u->entries[i].ind < v->entries[j].ind) ++i;
+ else if (u->entries[i].ind > v->entries[j].ind) ++j;
+ }
+}
diff --git a/fmpz_sparse_vec/dot_dense.c b/fmpz_sparse_vec/dot_dense.c
new file mode 100644
index 0000000000..1ac529bd80
--- /dev/null
+++ b/fmpz_sparse_vec/dot_dense.c
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_dot_dense(fmpz_t ret, const fmpz_sparse_vec_t u, const fmpz *v)
+{
+ slong i;
+ fmpz_zero(ret);
+ for (i = 0; i < u->nnz; ++i) fmpz_addmul(ret, u->entries[i].val, &v[u->entries[i].ind]);
+
+}
diff --git a/fmpz_sparse_vec/equal.c b/fmpz_sparse_vec/equal.c
new file mode 100644
index 0000000000..aec8f682d8
--- /dev/null
+++ b/fmpz_sparse_vec/equal.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+int fmpz_sparse_vec_equal(const fmpz_sparse_vec_t vec1, const fmpz_sparse_vec_t vec2, slong ioff)
+{
+ slong i;
+ if (vec1->nnz != vec2->nnz) return 0;
+ for (i = 0; i < vec1->nnz; ++i)
+ {
+ if ((vec1->entries[i].ind != vec2->entries[i].ind + ioff) ||
+ (!fmpz_equal(vec1->entries[i].val, vec2->entries[i].val))) return 0;
+ }
+ return 1;
+}
diff --git a/fmpz_sparse_vec/from_dense.c b/fmpz_sparse_vec/from_dense.c
new file mode 100644
index 0000000000..1689816c05
--- /dev/null
+++ b/fmpz_sparse_vec/from_dense.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_from_dense(fmpz_sparse_vec_t dst, const fmpz *src, slong len)
+{
+ slong i, nnz = 0;
+ fmpz_sparse_entry_struct *e;
+ for (i = 0; i < len; ++i)
+ if (!fmpz_is_zero(&src[i])) ++nnz;
+ _fmpz_sparse_vec_resize(dst, nnz);
+ e = dst->entries;
+ for (i = 0; i < len; ++i)
+ if (!fmpz_is_zero(&src[i]))
+ e->ind = i, fmpz_set(e->val, &src[i]), ++e;
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/from_entries.c b/fmpz_sparse_vec/from_entries.c
new file mode 100644
index 0000000000..4dd16974f3
--- /dev/null
+++ b/fmpz_sparse_vec/from_entries.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_from_entries(fmpz_sparse_vec_t vec, slong * inds, fmpz * vals, slong nnz)
+{
+ if (nnz == 0) fmpz_sparse_vec_clear(vec);
+ else {
+ slong i;
+ _fmpz_sparse_vec_resize(vec, nnz);
+ for (i = 0; i < nnz; ++i)
+ {
+ vec->entries[i].ind = inds[i];
+ fmpz_set(vec->entries[i].val, &vals[i]);
+ }
+ }
+}
diff --git a/fmpz_sparse_vec/gauss_elim.c b/fmpz_sparse_vec/gauss_elim.c
new file mode 100644
index 0000000000..a36dae5755
--- /dev/null
+++ b/fmpz_sparse_vec/gauss_elim.c
@@ -0,0 +1,122 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+/* Reduce u by v */
+void fmpz_sparse_vec_gauss_elim_col(fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, slong col)
+{
+ fmpz_t q;
+ fmpz_t *uc = fmpz_sparse_vec_at(u, col);
+ fmpz_t *vc = fmpz_sparse_vec_at(v, col);
+ if (uc == NULL || vc == NULL) return;
+
+ fmpz_init(q);
+ fmpz_fdiv_q(q, *uc, *vc);
+ fmpz_sparse_vec_scalar_submul_fmpz(u, u, v, q);
+ fmpz_clear(q);
+}
+
+/* Reduce u by v */
+void fmpz_sparse_vec_gauss_elim(fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v)
+{
+ fmpz_t q, *uc;
+ fmpz_sparse_entry_struct *lu = u->entries, *lv = v->entries;
+ if (u->nnz == 0 || v->nnz == 0 || lu->ind > lv->ind) return;
+ fmpz_init(q);
+ if (lu->ind == lv->ind) fmpz_fdiv_q(q, lu->val, lv->val);
+ else if ((uc = fmpz_sparse_vec_at(u, lv->ind))) fmpz_fdiv_q(q, *uc, lv->val);
+ fmpz_sparse_vec_scalar_submul_fmpz(u, u, v, q);
+ fmpz_clear(q);
+}
+
+/* Apply unimodular transformation to (u,v) to minimize both vectors lexicographically */
+void fmpz_sparse_vec_gauss_elim_ext(fmpz_sparse_vec_t u, fmpz_sparse_vec_t v)
+{
+ slong vnnz = v->nnz, unnz = u->nnz, nnz, k;
+ fmpz_sparse_entry_struct *lu = u->entries;
+ fmpz_sparse_entry_struct *lv = v->entries;
+ fmpz_t g, vv, vu, uv, uu, a, b;
+ fmpz_sparse_entry_struct *ue, *ve, *nue, *nve;
+ if (u->nnz == 0 || v->nnz == 0) return;
+ if (lu->ind != lv->ind) {fmpz_sparse_vec_gauss_elim(u, v); return;}
+ if (fmpz_cmpabs(lu->val, lv->val) < 0) /* Pre-apply transform [[0, -1], [1, 0]] */
+ {
+ fmpz_sparse_vec_swap(u, v);
+ fmpz_sparse_vec_neg(u, u);
+ lu = u->entries, lv = v->entries;
+ vnnz = v->nnz, unnz = u->nnz;
+ }
+ if (fmpz_sgn(lv->val) < 0) /* Pre-apply transform [[-1, 0], [0, -1]] */
+ {
+ fmpz_sparse_vec_neg(v, v);
+ fmpz_sparse_vec_neg(u, u);
+ }
+
+ /* Check for trivial cases */
+ if (fmpz_divisible(lu->val, lv->val)) {fmpz_sparse_vec_gauss_elim(u, v); return;}
+
+ fmpz_init(g);
+ fmpz_init(vv);
+ fmpz_init(vu);
+ fmpz_init(uv);
+ fmpz_init(uu);
+ fmpz_init(a);
+ fmpz_init(b);
+
+ /* Construct transformation */
+ fmpz_xgcd(g, vv, vu, lv->val, lu->val);
+ if (fmpz_sgn(g) < 0)
+ {
+ fmpz_neg(vv, vv);
+ fmpz_neg(vu, vu);
+ }
+ fmpz_divexact(uu, lv->val, g);
+ fmpz_divexact(uv, lu->val, g);
+ fmpz_neg(uv, uv); /* [[uu uv] [vu vv]] is unimodular */
+
+ /* Reallocate vectors */
+ nnz = _fmpz_sparse_vec_union_nnz (u, v);
+ _fmpz_sparse_vec_resize(u, nnz);
+ _fmpz_sparse_vec_resize(v, nnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz; /* Old locations */
+ nue = u->entries + nnz, nve = v->entries + nnz; /* New locations */
+ while ((k = _fmpz_sparse_vector_merge_descend (&nue, &ue, &ve, u, v)) >= 0)
+ {
+ nve--;
+ switch(k)
+ {
+ case 0: nve->ind = ue->ind; fmpz_mul(nve->val, ue->val, vu); fmpz_mul(nue->val, ue->val, uu); break;
+ case 1: nve->ind = ve->ind; fmpz_mul(nue->val, ve->val, uv); fmpz_mul(nve->val, ve->val, vv); break;
+ default: nve->ind = ve->ind;
+ fmpz_set(a, ve->val);
+ fmpz_set(b, ue->val);
+ fmpz_mul(nve->val, a, vv); fmpz_addmul(nve->val, b, vu);
+ fmpz_mul(nue->val, b, uu); fmpz_addmul(nue->val, a, uv);
+ }
+ if (fmpz_is_zero(nue->val)) nue++;
+ if (fmpz_is_zero(nve->val)) nve++;
+ }
+ _fmpz_sparse_vector_shift_left (u, nue - u->entries);
+ _fmpz_sparse_vector_shift_left (v, nve - v->entries);
+ fmpz_clear(g);
+ fmpz_clear(vv);
+ fmpz_clear(vu);
+ fmpz_clear(uv);
+ fmpz_clear(uu);
+ fmpz_clear(a);
+ fmpz_clear(b);
+}
diff --git a/fmpz_sparse_vec/max_bits.c b/fmpz_sparse_vec/max_bits.c
new file mode 100644
index 0000000000..e49ba507ea
--- /dev/null
+++ b/fmpz_sparse_vec/max_bits.c
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+
+slong fmpz_sparse_vec_max_bits(const fmpz_sparse_vec_t v)
+{
+ slong i, sign, max_limbs;
+ mp_limb_t max_limb;
+ mp_size_t limbs;
+
+ sign = 1;
+ max_limb = 0;
+
+ for (i = 0; i < v->nnz; i++)
+ {
+ fmpz c = *(v->entries[i].val);
+
+ if (c >= 0)
+ {
+ if (c > COEFF_MAX)
+ goto bignum;
+ max_limb |= c;
+ }
+ else
+ {
+ if (c < COEFF_MIN)
+ goto bignum;
+ max_limb |= -c;
+ sign = -1;
+ }
+ }
+ return sign * FLINT_BIT_COUNT(max_limb);
+
+bignum:
+ max_limbs = 1;
+
+ for ( ; i < v->nnz; i++)
+ {
+ fmpz c = *(v->entries[i].val);
+
+ if (COEFF_IS_MPZ(c))
+ {
+ __mpz_struct * z = COEFF_TO_PTR(c);
+ limbs = z->_mp_size;
+
+ if (limbs < 0)
+ {
+ sign = -1;
+ limbs = -limbs;
+ }
+
+ if (limbs == max_limbs)
+ max_limb |= z->_mp_d[limbs - 1];
+ else if (limbs > max_limbs)
+ {
+ max_limb = z->_mp_d[limbs - 1];
+ max_limbs = limbs;
+ }
+ }
+ else if (c < 0)
+ sign = -1;
+ }
+ return sign * ((max_limbs - 1) * FLINT_BITS + FLINT_BIT_COUNT(max_limb));
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/multi_CRT_ui.c b/fmpz_sparse_vec/multi_CRT_ui.c
new file mode 100644
index 0000000000..2e47171933
--- /dev/null
+++ b/fmpz_sparse_vec/multi_CRT_ui.c
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_vec.h"
+
+void
+fmpz_sparse_vec_multi_CRT_ui_precomp(fmpz_sparse_vec_t v, nmod_sparse_vec_struct const * residues, slong nres,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp, int sign)
+{
+ slong j, pos, *rpos, max_nnz, max_ind;
+ mp_limb_t *r;
+ fmpz_sparse_entry_struct *e;
+
+ for (j = max_nnz = max_ind = 0; j < nres; ++j)
+ {
+ max_nnz = FLINT_MAX(max_nnz, residues[j].nnz);
+ if (residues[j].nnz != 0) max_ind = FLINT_MAX(max_ind, residues[j].entries[residues[j].nnz - 1].ind);
+ }
+ _fmpz_sparse_vec_resize(v, max_nnz); /* May change later */
+ if (max_nnz == 0) return;
+
+ rpos = flint_calloc(nres, sizeof(*rpos));
+ r = flint_malloc(nres * sizeof (*r));
+ for (pos = 0; ; ++pos)
+ {
+ if (pos == max_nnz) max_nnz *= 2, _fmpz_sparse_vec_resize(v, max_nnz);
+ e = &v->entries[pos];
+
+ /* Get next minimal index */
+ e->ind = max_ind + 1;
+ for (j = 0; j < nres; ++j)
+ {
+ e->ind = (rpos[j] != residues[j].nnz && residues[j].entries[rpos[j]].ind < e->ind) ? residues[j].entries[rpos[j]].ind : e->ind;
+ }
+ if (e->ind == max_ind + 1) break;
+ for (j = 0; j < nres; ++j)
+ {
+ r[j] = (rpos[j] != residues[j].nnz && residues[j].entries[rpos[j]].ind == e->ind) ? residues[j].entries[rpos[j]++].val : 0;
+ }
+ fmpz_multi_CRT_ui(e->val, r, comb, temp, sign);
+ }
+ _fmpz_sparse_vec_resize(v, pos);
+ flint_free(rpos);
+ flint_free(r);
+}
+
+void
+fmpz_sparse_vec_multi_CRT_ui(fmpz_sparse_vec_t v, nmod_sparse_vec_struct * const residues, mp_srcptr primes, slong nres, int sign)
+{
+ fmpz_comb_t comb;
+ fmpz_comb_temp_t temp;
+
+ fmpz_comb_init(comb, primes, nres);
+ fmpz_comb_temp_init(temp, comb);
+
+ fmpz_sparse_vec_multi_CRT_ui_precomp(v, residues, nres, comb, temp, sign);
+
+ fmpz_comb_clear(comb);
+ fmpz_comb_temp_clear(temp);
+}
diff --git a/fmpz_sparse_vec/multi_mod_ui.c b/fmpz_sparse_vec/multi_mod_ui.c
new file mode 100644
index 0000000000..c52f665f75
--- /dev/null
+++ b/fmpz_sparse_vec/multi_mod_ui.c
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fmpz_sparse_vec.h"
+
+/* for (j = 0; j < fmpz_vec_ncols(vec); j++)
+ {
+
+ for (k = 0; k < nres; k++)
+ nmod_vec_entry(residues[k], i, j) = r[k];
+ }
+ */
+void
+fmpz_sparse_vec_multi_mod_ui_precomp(nmod_sparse_vec_struct * residues, slong nres, const fmpz_sparse_vec_t v,
+ const fmpz_comb_t comb, fmpz_comb_temp_t temp)
+{
+ slong i, j;
+ nmod_sparse_entry_struct *e;
+ nmod_sparse_vec_struct *vm;
+ mp_limb_t *r;
+
+ r = flint_malloc(nres * sizeof(*r));
+
+ for (j = 0; j < nres; ++j)
+ vm = &residues[j], vm->entries = realloc(vm->entries, v->nnz * sizeof(*vm->entries)), vm->nnz = 0;
+
+ for (i = 0; i < v->nnz; i++)
+ {
+ fmpz_multi_mod_ui(r, v->entries[i].val, comb, temp);
+ for (j = 0; j < nres; ++j)
+ if (r[j] != UWORD(0))
+ e = &residues[j].entries[residues[j].nnz++], e->ind = v->entries[i].ind, e->val = r[j];
+ }
+
+ for (j = 0; j < nres; ++j)
+ vm = &residues[j], vm->entries = realloc(vm->entries, vm->nnz * sizeof(*vm->entries));
+
+ flint_free(r);
+}
+
+void
+fmpz_sparse_vec_multi_mod_ui(nmod_sparse_vec_struct * residues, mp_srcptr primes, slong nres, const fmpz_sparse_vec_t v)
+{
+ fmpz_comb_t comb;
+ fmpz_comb_temp_t temp;
+
+ fmpz_comb_init(comb, primes, nres);
+ fmpz_comb_temp_init(temp, comb);
+
+ fmpz_sparse_vec_multi_mod_ui_precomp(residues, nres, v, comb, temp);
+
+ fmpz_comb_clear(comb);
+ fmpz_comb_temp_clear(temp);
+}
diff --git a/fmpz_sparse_vec/print_pretty.c b/fmpz_sparse_vec/print_pretty.c
new file mode 100644
index 0000000000..059237302d
--- /dev/null
+++ b/fmpz_sparse_vec/print_pretty.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_vec.h"
+#include "ulong_extras.h"
+
+void
+fmpz_sparse_vec_print_pretty(const fmpz_sparse_vec_t vec, slong ioff, slong maxi)
+{
+ slong i;
+ char ind_fmt[FLINT_BITS + 5];
+
+ flint_sprintf(ind_fmt, "%%%dwd:", n_sizeinbase(maxi, 10));
+
+ flint_printf("[");
+ for (i = 0; i < vec->nnz; i++)
+ {
+ flint_printf(ind_fmt, vec->entries[i].ind - ioff);
+ fmpz_print(vec->entries[i].val);
+ if (i + 1 < vec->nnz) flint_printf(" ");
+ }
+ flint_printf("]\n");
+}
+
diff --git a/fmpz_sparse_vec/randtest.c b/fmpz_sparse_vec/randtest.c
new file mode 100644
index 0000000000..9a46c0a7df
--- /dev/null
+++ b/fmpz_sparse_vec/randtest.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_randtest(fmpz_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, flint_bitcnt_t bits)
+{
+ slong i, j;
+ nnz = FLINT_MIN(nnz, len);
+ _fmpz_sparse_vec_resize(vec, nnz);
+ for (i = 0; i < nnz; ++i)
+ {
+ vec->entries[i].ind = i;
+ do fmpz_randtest(vec->entries[i].val, state, bits);
+ while (fmpz_is_zero(vec->entries[i].val));
+ }
+
+ /* Use resevoir sampling to get random support */
+ for (j = nnz; j < len; ++j)
+ {
+ i = n_randint(state, j+1);
+ if (i < nnz) vec->entries[i].ind = j;
+ }
+ qsort(vec->entries, nnz, sizeof(*vec->entries), fmpz_sparse_entry_cmp);
+}
diff --git a/fmpz_sparse_vec/randtest_unsigned.c b/fmpz_sparse_vec/randtest_unsigned.c
new file mode 100644
index 0000000000..457e1486c3
--- /dev/null
+++ b/fmpz_sparse_vec/randtest_unsigned.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_randtest_unsigned(fmpz_sparse_vec_t vec, flint_rand_t state, slong nnz, slong len, flint_bitcnt_t bits)
+{
+ slong i, j;
+ nnz = FLINT_MIN(nnz, len);
+ _fmpz_sparse_vec_resize(vec, nnz);
+ for (i = 0; i < nnz; ++i)
+ {
+ vec->entries[i].ind = i;
+ do fmpz_randtest_unsigned(vec->entries[i].val, state, bits);
+ while (fmpz_is_zero(vec->entries[i].val));
+ }
+
+ /* Use resevoir sampling to get random support */
+ for (j = nnz; j < len; ++j)
+ {
+ i = n_randint(state, j+1);
+ if (i < nnz) vec->entries[i].ind = j;
+ }
+ qsort(vec->entries, nnz, sizeof(*vec->entries), fmpz_sparse_entry_cmp);
+}
diff --git a/fmpz_sparse_vec/scalar_addmul.c b/fmpz_sparse_vec/scalar_addmul.c
new file mode 100644
index 0000000000..df39c6aaaf
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_addmul.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_addmul_fmpz(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, const fmpz_t c)
+{
+ slong unnz = u->nnz, vnnz = v->nnz, wnnz, k;
+ fmpz_t tmp;
+ fmpz_sparse_entry_struct *ue, *ve, *we;
+
+ /* Check for simpler operations first */
+ if (fmpz_is_zero(c) || vnnz == 0) fmpz_sparse_vec_set(w, u, 0);
+ else if (fmpz_is_one(c)) fmpz_sparse_vec_add(w, u, v);
+ else if (fmpz_equal_si(c, WORD(-1))) fmpz_sparse_vec_sub(w, u, v);
+ else if (unnz == 0) fmpz_sparse_vec_scalar_mul_fmpz(w, v, c);
+ else
+ {
+ fmpz_init(tmp);
+ wnnz = _fmpz_sparse_vec_union_nnz (u, v);
+ _fmpz_sparse_vec_resize(w, wnnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz, we = w->entries + wnnz;
+ while ((k = _fmpz_sparse_vector_merge_descend (&we, &ue, &ve, u, v)) >= 0)
+ {
+ switch(k)
+ {
+ case 0: fmpz_set(we->val, ue->val); break;
+ case 1: fmpz_mul(we->val, ve->val, c); break;
+ default: fmpz_mul(tmp, ve->val, c);
+ fmpz_add(we->val, ue->val, tmp);
+ if (fmpz_is_zero(we->val)) we++;
+ }
+ }
+ _fmpz_sparse_vector_shift_left (w, we - w->entries);
+ fmpz_clear(tmp);
+ }
+}
diff --git a/fmpz_sparse_vec/scalar_divexact.c b/fmpz_sparse_vec/scalar_divexact.c
new file mode 100644
index 0000000000..c584202c97
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_divexact.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_divexact_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t c)
+{
+ if (fmpz_is_one(c)) fmpz_sparse_vec_set(v, u, 0);
+ else if (fmpz_equal_si(c, WORD(-1))) fmpz_sparse_vec_neg(v, u);
+ else
+ {
+ slong i;
+ fmpz_sparse_vec_set(v, u, 0);
+ for (i = 0; i < v->nnz; ++i) fmpz_divexact(v->entries[i].val, v->entries[i].val, c);
+ }
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/scalar_mod.c b/fmpz_sparse_vec/scalar_mod.c
new file mode 100644
index 0000000000..5fce2ba885
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_mod.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_mod_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t mod)
+{
+ if (fmpz_is_one(mod)) fmpz_sparse_vec_zero(v);
+ else
+ {
+ slong i, ind;
+ fmpz_sparse_vec_set(v, u, 0);
+ for (i = ind = 0; i < v->nnz; ++i)
+ {
+ v->entries[ind].ind = v->entries[i].ind;
+ fmpz_mod(v->entries[ind].val, v->entries[i].val, mod);
+ if (!fmpz_is_zero(v->entries[ind].val)) ++ind;
+ }
+ _fmpz_sparse_vec_resize(v, ind);
+ }
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/scalar_mods.c b/fmpz_sparse_vec/scalar_mods.c
new file mode 100644
index 0000000000..94c8409069
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_mods.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_mods_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t mod)
+{
+ if (fmpz_is_one(mod)) fmpz_sparse_vec_zero(v);
+ else
+ {
+ slong i, ind;
+ fmpz_sparse_vec_set(v, u, 0);
+ for (i = ind = 0; i < v->nnz; ++i)
+ {
+ v->entries[ind].ind = v->entries[i].ind;
+ fmpz_smod(v->entries[ind].val, v->entries[i].val, mod);
+ if (!fmpz_is_zero(v->entries[ind].val)) ++ind;
+ }
+ _fmpz_sparse_vec_resize(v, ind);
+ }
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/scalar_mul.c b/fmpz_sparse_vec/scalar_mul.c
new file mode 100644
index 0000000000..e51ea0dace
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_mul.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_mul_fmpz(fmpz_sparse_vec_t v, const fmpz_sparse_vec_t u, const fmpz_t c)
+{
+ if (fmpz_is_zero(c)) fmpz_sparse_vec_zero(v);
+ else if (fmpz_is_one(c)) fmpz_sparse_vec_set(v, u, 0);
+ else if (fmpz_equal_si(c, WORD(-1))) fmpz_sparse_vec_neg(v, u);
+ else
+ {
+ slong i;
+ fmpz_sparse_vec_set(v, u, 0);
+ for (i = 0; i < v->nnz; ++i) fmpz_mul(v->entries[i].val, v->entries[i].val, c);
+ }
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/scalar_submul.c b/fmpz_sparse_vec/scalar_submul.c
new file mode 100644
index 0000000000..82c9860a8a
--- /dev/null
+++ b/fmpz_sparse_vec/scalar_submul.c
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_scalar_submul_fmpz(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v, const fmpz_t c)
+{
+ slong unnz = u->nnz, vnnz = v->nnz, wnnz, k;
+ fmpz_t tmp, tmp2;
+ fmpz_sparse_entry_struct *ue, *ve, *we;
+
+/* flint_printf("c = "), fmpz_print(c); flint_printf("\n"); */
+ /* Check for simpler operations first */
+ if (fmpz_is_zero(c) || vnnz == 0) fmpz_sparse_vec_set(w, u, 0);
+ else if (fmpz_is_one(c)) fmpz_sparse_vec_sub(w, u, v);
+ else if (fmpz_equal_si(c, WORD(-1))) fmpz_sparse_vec_add(w, u, v);
+ else if (unnz == 0)
+ {
+ fmpz_sparse_vec_scalar_mul_fmpz(w, v, c);
+ fmpz_sparse_vec_neg(w, w);
+ }
+ else
+ {
+ fmpz_init(tmp);
+ fmpz_init_set(tmp2, c);
+ wnnz = _fmpz_sparse_vec_union_nnz (u, v);
+ _fmpz_sparse_vec_resize(w, wnnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz, we = w->entries + wnnz;
+ /* flint_printf("c = "), fmpz_print(c); flint_printf(": %wd\n", c[0]); */
+ while ((k = _fmpz_sparse_vector_merge_descend (&we, &ue, &ve, u, v)) >= 0)
+ {
+/* flint_printf("\t\t\tc = "), fmpz_print(c); flint_printf("\n");
+ flint_printf("\t\t\ttmp = %wd, tmp2 = %wd\n", tmp[0], tmp2[0]); */
+ switch(k)
+ {
+ case 0: fmpz_set(we->val, ue->val); break;
+ case 1: fmpz_mul(we->val, ve->val, tmp2); fmpz_neg(we->val, we->val); break;
+ default:
+/* flint_printf("\t\tSubtracting: ");
+ fmpz_print(we->val); flint_printf(" <- ");
+ fmpz_print(ue->val); flint_printf("-");
+ fmpz_print(ve->val); flint_printf("*"); fmpz_print(c); flint_printf("\n");
+ flint_printf("%wd <- %wd - %wd * %wd\n", we->val[0], ue->val[0], ve->val[0], c[0]);
+ flint_printf("c1: "); fmpz_print(c); flint_printf("\n"); */
+ fmpz_mul(tmp, ve->val, tmp2);
+/* flint_printf("%wd = %wd * %wd\n", tmp[0], ve->val[0], c[0]);
+ flint_printf("c2: "); fmpz_print(c); flint_printf("\n"); */
+ fmpz_sub(we->val, ue->val, tmp);
+/* flint_printf("%wd = %wd - %wd, %wd\n", we->val[0], ue->val[0], tmp[0], c[0]);
+ flint_printf("c3: "); fmpz_print(c); flint_printf("\n"); */
+ if (fmpz_is_zero(we->val)) we++;
+/* flint_printf("c4: "); fmpz_print(c); flint_printf("\n"); */
+ }
+/* flint_printf("\t\tAfter subtraction: %wd, %wd\n", k, we->ind);
+ flint_printf("\t\t\tr: "); fmpz_sparse_vec_print_pretty(w, 0, 0); flint_printf("\n"); */
+ }
+ _fmpz_sparse_vector_shift_left (w, we - w->entries);
+ fmpz_clear(tmp);
+ fmpz_clear(tmp2);
+ }
+}
diff --git a/fmpz_sparse_vec/set.c b/fmpz_sparse_vec/set.c
new file mode 100644
index 0000000000..6e84f479db
--- /dev/null
+++ b/fmpz_sparse_vec/set.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_set(fmpz_sparse_vec_t dst, const fmpz_sparse_vec_t src, slong ioff)
+{
+ slong i;
+ if (dst == src) return;
+ _fmpz_sparse_vec_resize(dst, src->nnz);
+ for (i = 0; i < dst->nnz; ++i)
+ {
+ dst->entries[i].ind = src->entries[i].ind - ioff;
+ fmpz_set(dst->entries[i].val, src->entries[i].val);
+ }
+}
\ No newline at end of file
diff --git a/fmpz_sparse_vec/split.c b/fmpz_sparse_vec/split.c
new file mode 100644
index 0000000000..6a3582cfc0
--- /dev/null
+++ b/fmpz_sparse_vec/split.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_split(fmpz_sparse_vec_t res1, fmpz_sparse_vec_t res2, const fmpz_sparse_vec_t vec, slong ind)
+{
+ slong i, nnz1;
+ fmpz_sparse_entry_struct *e1, *e2, *e;
+ for (nnz1 = 0; nnz1 < vec->nnz; ++nnz1) if (vec->entries[nnz1].ind >= ind) break;
+
+ _fmpz_sparse_vec_resize(res1, nnz1);
+ _fmpz_sparse_vec_resize(res2, vec->nnz - nnz1);
+ e1 = res1->entries, e2 = res2->entries;
+ for (i = 0; i < vec->nnz; ++i)
+ {
+ e = (i < nnz1) ? e1++ : e2++;
+ e->ind = vec->entries[i].ind - ((i < nnz1) ? 0 : ind);
+ fmpz_set(e->val, vec->entries[i].val);
+ }
+}
diff --git a/fmpz_sparse_vec/sub.c b/fmpz_sparse_vec/sub.c
new file mode 100644
index 0000000000..f0dfe72e06
--- /dev/null
+++ b/fmpz_sparse_vec/sub.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_sub(fmpz_sparse_vec_t w, const fmpz_sparse_vec_t u, const fmpz_sparse_vec_t v)
+{
+ slong unnz = u->nnz, vnnz = v->nnz, wnnz, k;
+ fmpz_sparse_entry_struct *ue, *ve, *we;
+
+ /* Check for simpler operations first */
+ if (vnnz == 0) fmpz_sparse_vec_set(w, u, 0);
+ else if (unnz == 0) fmpz_sparse_vec_neg(w, v);
+ else
+ {
+ wnnz = _fmpz_sparse_vec_union_nnz (u, v);
+ _fmpz_sparse_vec_resize(w, wnnz);
+ ue = u->entries + unnz, ve = v->entries + vnnz, we = w->entries + wnnz;
+ while ((k = _fmpz_sparse_vector_merge_descend (&we, &ue, &ve, u, v)) >= 0)
+ {
+ switch(k)
+ {
+ case 0: fmpz_set(we->val, ue->val); break;
+ case 1: fmpz_neg(we->val, ve->val); break;
+ default: fmpz_sub(we->val, ue->val, ve->val);
+ if (fmpz_is_zero(we->val)) we++;
+ }
+ }
+ _fmpz_sparse_vector_shift_left (w, we - w->entries);
+ }
+}
diff --git a/fmpz_sparse_vec/test/t-add.c b/fmpz_sparse_vec/test/t-add.c
new file mode 100644
index 0000000000..797afc9aee
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-add.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_sparse_vec_t u, v, w, x;
+
+ FLINT_TEST_INIT(state);
+
+ flint_printf("add/sub....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 256);
+ while (bits < UWORD(2));
+ len = n_randint(state, 200);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+ fmpz_sparse_vec_init(x);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ fmpz_sparse_vec_add(w, u, v);
+ fmpz_sparse_vec_sub(x, w, v);
+
+ if (!fmpz_sparse_vec_equal(u, x, 0))
+ {
+ flint_printf("FAIL: u != u+v-v\n");
+ flint_printf("u = "), fmpz_sparse_vec_print_pretty(u, 0, 0);
+ flint_printf("v = "), fmpz_sparse_vec_print_pretty(v, 0, 0);
+ flint_printf("w = "), fmpz_sparse_vec_print_pretty(w, 0, 0);
+ flint_printf("x = "), fmpz_sparse_vec_print_pretty(x, 0, 0);
+ abort();
+ }
+
+ fmpz_sparse_vec_add(u, u, v);
+ if (!fmpz_sparse_vec_equal(u, w, 0))
+ {
+ flint_printf("FAIL: (u += v) != u + v\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_sub(u, u, v);
+ if (!fmpz_sparse_vec_equal(u, x, 0))
+ {
+ flint_printf("FAIL: ((u += v) -= v) != u+v-v\n");
+ abort();
+ }
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ fmpz_sparse_vec_clear(x);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-concat.c b/fmpz_sparse_vec/test/t-concat.c
new file mode 100644
index 0000000000..3a1dbd483e
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-concat.c
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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_sparse_vec.h"
+#include "ulong_extras.h"
+
+int main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_sparse_vec_t u, v, w;
+ fmpz_sparse_vec_t window1, window2;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("concat....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 100; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 200);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(w, state, nnz, len, bits);
+
+ fmpz_sparse_vec_concat(w, u, v, len);
+
+ fmpz_sparse_vec_window_init(window1, w, 0, len);
+ fmpz_sparse_vec_window_init(window2, w, len, 2*len);
+
+ if (!(fmpz_sparse_vec_equal(window1, u, 0) && fmpz_sparse_vec_equal(window2, v, len)))
+ {
+ flint_printf("u = ");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ flint_printf("v = \n");
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ flint_printf("u | v = \n");
+ fmpz_sparse_vec_print_pretty(w, 0, len);
+ flint_printf("window1 = \n");
+ fmpz_sparse_vec_print_pretty(window1, 0, len);
+ flint_printf("window2 = \n");
+ fmpz_sparse_vec_print_pretty(window2, len, len);
+ flint_printf("FAIL: results not equal\n");
+ abort();
+ }
+ fmpz_sparse_vec_window_clear(window1);
+ fmpz_sparse_vec_window_clear(window2);
+
+ fmpz_sparse_vec_init(window1);
+ fmpz_sparse_vec_init(window2);
+ fmpz_sparse_vec_split(window1, window2, w, len);
+ if (!(fmpz_sparse_vec_equal(window1, u, 0) && fmpz_sparse_vec_equal(window2, v, 0)))
+ {
+ flint_printf("u = ");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ flint_printf("v = \n");
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ flint_printf("u | v = \n");
+ fmpz_sparse_vec_print_pretty(w, 0, len);
+ flint_printf("window1 = \n");
+ fmpz_sparse_vec_print_pretty(window1, 0, len);
+ flint_printf("window2 = \n");
+ fmpz_sparse_vec_print_pretty(window2, 0, len);
+ flint_printf("FAIL: results not equal\n");
+ abort();
+ }
+ fmpz_sparse_vec_clear(window1);
+ fmpz_sparse_vec_clear(window2);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ }
+
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-construct.c b/fmpz_sparse_vec/test/t-construct.c
new file mode 100644
index 0000000000..fdc2381532
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-construct.c
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz, i;
+ fmpz_sparse_vec_t u, v;
+ slong *inds;
+ fmpz *vals;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("construction from elements....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ len = n_randint(state, 10);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ /* Construct v from entries of u */
+ inds = flint_malloc(nnz * sizeof(*inds));
+ vals = flint_malloc(nnz * sizeof(*vals));
+ for (i = 0; i < nnz; ++i)
+ {
+ fmpz_init(&vals[i]);
+ inds[i] = u->entries[i].ind;
+ fmpz_set(&vals[i], u->entries[i].val);
+ }
+ fmpz_sparse_vec_from_entries(v, inds, vals, nnz);
+
+ if (!fmpz_sparse_vec_equal(u, v, 0))
+ {
+ fmpz_sparse_vec_print_pretty(u, 0, 0);
+ fmpz_sparse_vec_print_pretty(v, 0, 0);
+ abort();
+ }
+ flint_free(inds);
+ for (i = 0; i < nnz; ++i)
+ {
+ fmpz_clear(&vals[i]);
+ }
+ flint_free(vals);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-dense.c b/fmpz_sparse_vec/test/t-dense.c
new file mode 100644
index 0000000000..ce84cffa53
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-dense.c
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz, i;
+ fmpz_sparse_vec_t u, v;
+ fmpz *w, *x;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("conversion to/from dense vector....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 200);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ w = _fmpz_vec_init(len);
+ x = _fmpz_vec_init(len);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ fmpz_sparse_vec_to_dense(w, u, len);
+ fmpz_sparse_vec_from_dense(v, w, len);
+
+ for (i = 0; i < len; ++i)
+ {
+ fmpz_t *val = fmpz_sparse_vec_at(u, i);
+ if ((fmpz_is_zero(&w[i]) && val != NULL) || (!fmpz_is_zero(&w[i]) && (val == NULL || !fmpz_equal(*val, &w[i]))))
+ {
+ flint_printf("FAIL: u[%wd] != v[%wd]\n", i, i);
+ abort();
+ }
+ }
+ if (!fmpz_sparse_vec_equal(u, v, 0))
+ {
+ flint_printf("FAIL: u != v\n");
+ abort();
+ }
+
+ _fmpz_vec_randtest(w, state, len, bits);
+ fmpz_sparse_vec_from_dense(u, w, len);
+ fmpz_sparse_vec_to_dense(x, u, len);
+
+ if (!_fmpz_vec_equal(w, x, len))
+ {
+ flint_printf("FAIL: w != x\n");
+ abort();
+ }
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ _fmpz_vec_clear(w, len);
+ _fmpz_vec_clear(x, len);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-dot.c b/fmpz_sparse_vec/test/t-dot.c
new file mode 100644
index 0000000000..87be4e5b20
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-dot.c
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_t a, b;
+ fmpz_sparse_vec_t u, v;
+ fmpz *w, *x;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("dot product....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ w = _fmpz_vec_init(len);
+ x = _fmpz_vec_init(len);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+ fmpz_sparse_vec_to_dense(w, u, len);
+ fmpz_sparse_vec_to_dense(x, v, len);
+
+ fmpz_sparse_vec_dot(a, u, v);
+ _fmpz_vec_dot(b, w, x, len);
+
+ if (!fmpz_equal(a, b))
+ {
+ flint_printf("Fail: sparse dot sparse\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_dot_dense(a, u, x);
+
+ if (!fmpz_equal(a, b))
+ {
+ flint_printf("Fail: sparse dot dense\n");
+ abort();
+ }
+ fmpz_clear(a);
+ fmpz_clear(b);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ _fmpz_vec_clear(w, len);
+ _fmpz_vec_clear(x, len);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-gauss_elim.c b/fmpz_sparse_vec/test/t-gauss_elim.c
new file mode 100644
index 0000000000..0b60a408c4
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-gauss_elim.c
@@ -0,0 +1,133 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "fmpz_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_t a, b, c, d, g;
+ fmpz_sparse_vec_t u, v, w, x, y;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("Gaussian elimination....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 10; rep++)
+ {
+ do bits = n_randint(state, 10);
+ while (bits < UWORD(2));
+ len = n_randint(state, 20);
+ nnz = n_randint(state, len+1);
+
+ fmpz_init(a);
+ fmpz_init(b);
+ fmpz_init(c);
+ fmpz_init(d);
+ fmpz_init(g);
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+ fmpz_sparse_vec_init(x);
+ fmpz_sparse_vec_init(y);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+ fmpz_sparse_vec_set(w, u, 0);
+ fmpz_sparse_vec_set(x, v, 0);
+ fmpz_sparse_vec_gauss_elim(x, w);
+ if (!fmpz_sparse_vec_equal(w, u, 0))
+ {
+ flint_printf("FAIL: pivot row modified\n");
+ abort();
+ }
+ if (nnz != 0 && fmpz_sparse_vec_at(v, u->entries[0].ind) != NULL)
+ {
+ /* Undo elimination and check if matches original */
+ fmpz_fdiv_q(c, *fmpz_sparse_vec_at(v, u->entries[0].ind), u->entries[0].val);
+ fmpz_sparse_vec_scalar_addmul_fmpz(w, x, u, c);
+ if (!fmpz_sparse_vec_equal(w, v, 0))
+ {
+ flint_printf("FAIL: Incorrect Gaussian elimination\n");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ fmpz_sparse_vec_print_pretty(x, 0, len);
+ fmpz_sparse_vec_print_pretty(w, 0, len);
+ abort();
+ }
+ }
+ else if (!fmpz_sparse_vec_equal(x, v, 0))
+ {
+ flint_printf("FAIL: Incorrect Gaussian elimination on trivial vectors\n");
+ fmpz_sparse_vec_print_pretty(x, 0, len);
+ abort();
+ }
+
+ if (nnz != 0 && u->entries[0].ind == v->entries[0].ind)
+ {
+ fmpz_sparse_vec_set(w, u, 0);
+ fmpz_sparse_vec_set(x, v, 0);
+ fmpz_sparse_vec_gauss_elim_ext(x, w);
+
+ fmpz_xgcd(g, a, b, u->entries[0].val, v->entries[0].val);
+ fmpz_divexact(d, u->entries[0].val, g);
+ fmpz_divexact(c, v->entries[0].val, g);
+ fmpz_sparse_vec_scalar_mul_fmpz(y, w, d);
+ fmpz_sparse_vec_scalar_submul_fmpz(y, y, x, b);
+ if (!fmpz_sparse_vec_equal(y, u, 0))
+ {
+ flint_printf("FAIL: did not recover u when inverting extended Gaussian elimination\n");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ fmpz_sparse_vec_print_pretty(w, 0, len);
+ fmpz_sparse_vec_print_pretty(x, 0, len);
+ fmpz_sparse_vec_print_pretty(y, 0, len);
+ abort();
+ }
+ fmpz_sparse_vec_scalar_mul_fmpz(y, w, c);
+ fmpz_sparse_vec_scalar_addmul_fmpz(y, y, x, a);
+ if (!fmpz_sparse_vec_equal(y, v, 0))
+ {
+ flint_printf("FAIL: did not recover v when inverting extended Gaussian elimination\n");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ fmpz_sparse_vec_print_pretty(w, 0, len);
+ fmpz_sparse_vec_print_pretty(x, 0, len);
+ fmpz_sparse_vec_print_pretty(y, 0, len);
+ abort();
+ }
+ }
+ fmpz_clear(a);
+ fmpz_clear(b);
+ fmpz_clear(c);
+ fmpz_clear(d);
+ fmpz_clear(g);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ fmpz_sparse_vec_clear(x);
+ fmpz_sparse_vec_clear(y);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-init_clear.c b/fmpz_sparse_vec/test/t-init_clear.c
new file mode 100644
index 0000000000..27deb5c8cd
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-init_clear.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+static void check_zero(fmpz_sparse_vec_t vec)
+{
+ if (vec->nnz != UWORD(0))
+{
+ flint_printf("FAIL: nnz not zero!\n");
+ abort();
+ }
+ if (vec->entries != NULL)
+{
+ flint_printf("FAIL: entries not null!\n");
+ abort();
+ }
+}
+
+int
+main(void)
+{
+ slong rep, len, nnz, bits, i;
+ fmpz_sparse_vec_t vec;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("init/clear....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+ fmpz_sparse_vec_init(vec);
+ check_zero(vec);
+
+ fmpz_sparse_vec_randtest(vec, state, nnz, len, bits);
+
+ if (nnz == 0) check_zero(vec);
+ else
+ {
+ for (i = 0; i < nnz; ++i)
+ {
+ fmpz_sparse_entry_struct *e = &vec->entries[i];
+ if (e->ind >= len)
+ {
+ flint_printf("FAIL: found index %wd >= %wd!\n", e->ind, len);
+ abort();
+ }
+ if (fmpz_is_zero(e->val))
+ {
+ flint_printf("FAIL: found zero value\n");
+ abort();
+ }
+ if (i > 0 && e->ind <= e[-1].ind)
+ {
+ flint_printf("FAIL: found index %wd <= previous index %wd\n", e->ind, e[-1].ind);
+ abort();
+ }
+ }
+ }
+
+ fmpz_sparse_vec_clear(vec);
+ check_zero(vec);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-max_bits.c b/fmpz_sparse_vec/test/t-max_bits.c
new file mode 100644
index 0000000000..eceff4a41e
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-max_bits.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, mbits, max_bits, len, nnz, i;
+ fmpz_sparse_vec_t u;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("max bits....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 200);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ mbits = fmpz_sparse_vec_max_bits(u);
+ mbits = FLINT_ABS(mbits);
+
+ max_bits = 0;
+ for (i = 0; i < nnz; ++i)
+ {
+ bits = fmpz_bits(u->entries[i].val);
+ if (bits > mbits)
+ {
+ flint_printf("FAIL: entry %wd has bitcnt %wd, max was %wd\n", i, bits, mbits);
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ abort();
+ }
+ if (bits > max_bits) max_bits = bits;
+ }
+ if (mbits != max_bits)
+ {
+ flint_printf("FAIL: no entry has bitcnt %wd\n", mbits);
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ abort();
+ }
+ fmpz_sparse_vec_clear(u);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-neg.c b/fmpz_sparse_vec/test/t-neg.c
new file mode 100644
index 0000000000..18c4bf2f50
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-neg.c
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_sparse_vec_t u, v, w, x;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("neg....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 200);
+ nnz = n_randint(state, len+1);
+
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+ fmpz_sparse_vec_init(x);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ fmpz_sparse_vec_sub(w, u, v);
+ fmpz_sparse_vec_neg(v, v);
+ fmpz_sparse_vec_add(x, u, v);
+
+ if (!fmpz_sparse_vec_equal(w, x, 0))
+ {
+ flint_printf("FAIL: u - v != u + (-v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_neg(w, u);
+ fmpz_sparse_vec_neg(u, u);
+
+ if (!fmpz_sparse_vec_equal(w, w, 0))
+ {
+ flint_printf("FAIL\n");
+ abort();
+ }
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ fmpz_sparse_vec_clear(x);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-nmod.c b/fmpz_sparse_vec/test/t-nmod.c
new file mode 100644
index 0000000000..7e19fc03dc
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-nmod.c
@@ -0,0 +1,118 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz, i, j;
+ slong n, c;
+ nmod_t mod;
+ nmod_sparse_vec_t umod;
+ fmpz_sparse_vec_t u;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("nmod....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 256);
+ while (bits < UWORD(2));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+
+ do n = n_randlimb(state);
+ while (n < UWORD(2));
+ nmod_init(&mod, n);
+
+ nmod_sparse_vec_init(umod);
+ fmpz_sparse_vec_init(u);
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_get_nmod_sparse_vec(umod, u, mod);
+
+ for (i = j = 0; i < u->nnz; ++i)
+ {
+ c = fmpz_fdiv_ui(u->entries[i].val, n);
+ if (c == UWORD(0)) continue;
+ if (u->entries[i].ind != umod->entries[j].ind)
+ {
+ flint_printf("Fail: v %% c has extra/missing entry\n");
+ abort();
+ }
+ if (c != umod->entries[j].val)
+ {
+ flint_printf("Fail: v %% c has incorrect entry\n");
+ abort();
+ }
+ ++j;
+ }
+ if (j != umod->nnz)
+ {
+ flint_printf("Fail: v %% c has incorrect number of nonzeroes\n");
+ abort();
+ }
+
+ nmod_sparse_vec_randtest(umod, state, nnz, len, mod);
+ fmpz_sparse_vec_set_nmod_sparse_vec_unsigned(u, umod);
+
+ for (i = 0; i < u->nnz; ++i)
+ {
+ if (u->entries[i].ind != umod->entries[i].ind ||
+ fmpz_cmp_ui(u->entries[i].val, umod->entries[i].val))
+ {
+ flint_printf("FAIL: u = umod has bad entry\n");
+ abort();
+ }
+ }
+ if (u->nnz != umod->nnz)
+ {
+ flint_printf("Fail: u = umod has bad length\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_set_nmod_sparse_vec(u, umod, mod);
+
+ for (i = 0; i < u->nnz; ++i)
+ {
+ if (u->entries[i].ind != umod->entries[i].ind ||
+ (fmpz_sgn(u->entries[i].val) > 0 && fmpz_cmp_ui(u->entries[i].val, umod->entries[i].val)) ||
+ (fmpz_sgn(u->entries[i].val) < 0 && fmpz_cmp_si(u->entries[i].val, umod->entries[i].val - n)))
+ {
+ flint_printf("FAIL: su = umod has bad entry\n");
+ abort();
+ }
+ }
+ if (u->nnz != umod->nnz)
+ {
+ flint_printf("Fail: su = umod has bad length\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_clear(u);
+ nmod_sparse_vec_clear(umod);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-scalar_divexact.c b/fmpz_sparse_vec/test/t-scalar_divexact.c
new file mode 100644
index 0000000000..06cca65702
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-scalar_divexact.c
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "fmpz_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_t c;
+ fmpz_sparse_vec_t u, v, w;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("divexact....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1; rep++)
+ {
+ do bits = n_randint(state, 100);
+ while (bits < UWORD(2));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+
+ fmpz_init(c);
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+
+ do fmpz_randtest(c, state, bits);
+ while (fmpz_is_zero(c));
+
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ fmpz_sparse_vec_scalar_mul_fmpz(v, u, c);
+ fmpz_sparse_vec_scalar_divexact_fmpz(w, v, c);
+
+ if (!fmpz_sparse_vec_equal(w, u, 0))
+ {
+ flint_printf("FAIL: out of place c*v/c != v\n");
+ flint_printf("rep = %wd, c = ", rep), fmpz_print(c), flint_printf("\n");
+ fmpz_sparse_vec_print_pretty(u, 0, 0);
+ fmpz_sparse_vec_print_pretty(v, 0, 0);
+ fmpz_sparse_vec_print_pretty(w, 0, 0);
+ fmpz_clear(c);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ abort();
+ }
+ fmpz_sparse_vec_scalar_divexact_fmpz(v, v, c);
+
+ if (!fmpz_sparse_vec_equal(v, u, 0))
+ {
+ flint_printf("FAIL: in place c*v/c != v\n");
+ flint_printf("c = "), fmpz_print(c), flint_printf("\n");
+ fmpz_sparse_vec_print_pretty(u, 0, 0);
+ fmpz_sparse_vec_print_pretty(v, 0, 0);
+ fmpz_clear(c);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ abort();
+ }
+ fmpz_clear(c);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-scalar_mod.c b/fmpz_sparse_vec/test/t-scalar_mod.c
new file mode 100644
index 0000000000..3f8d026a27
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-scalar_mod.c
@@ -0,0 +1,161 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz, i, j;
+ fmpz_t mod, c;
+ fmpz_sparse_vec_t u, v;
+ FLINT_TEST_INIT(state);
+
+
+ flint_printf("mod....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 10);
+ while (bits < UWORD(3));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+
+ fmpz_init(mod);
+ fmpz_init(c);
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+
+ fmpz_randbits(mod, state, bits-1);
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+
+ fmpz_sparse_vec_scalar_mod_fmpz(v, u, mod);
+
+ for (i = j = 0; i < u->nnz; ++i)
+ {
+ fmpz_mod(c, u->entries[i].val, mod);
+ if (fmpz_is_zero(c)) continue;
+ if (u->entries[i].ind != v->entries[j].ind)
+ {
+ flint_printf("Fail: v %% c has extra/missing entry\n");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ fmpz_print(mod); flint_printf("\n");
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ abort();
+ }
+ if (fmpz_cmp(c, v->entries[j].val) != 0)
+ {
+ flint_printf("Fail: v %% c has incorrect entry\n");
+ abort();
+ }
+ ++j;
+ }
+ if (j != v->nnz)
+ {
+ flint_printf("Fail: v %% c has incorrect number of nonzeroes\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_mods_fmpz(v, u, mod);
+ for (i = j = 0; i < u->nnz; ++i)
+ {
+ fmpz_smod(c, u->entries[i].val, mod);
+ if (fmpz_is_zero(c)) continue;
+ if (u->entries[i].ind != v->entries[j].ind)
+ {
+ flint_printf("Fail: v %%s c has extra/missing entry\n");
+ fmpz_sparse_vec_print_pretty(u, 0, len);
+ fmpz_print(mod); flint_printf("\n");
+ fmpz_sparse_vec_print_pretty(v, 0, len);
+ abort();
+ }
+ if (fmpz_cmp(c, v->entries[j].val) != 0)
+ {
+ flint_printf("Fail: v %%s c has incorrect entry\n");
+ abort();
+ }
+ ++j;
+ }
+ if (j != v->nnz)
+ {
+ flint_printf("Fail: v %%s c has incorrect number of nonzeroes\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_set(v, u, 0);
+ fmpz_sparse_vec_scalar_mod_fmpz(v, v, mod);
+
+ for (i = j = 0; i < u->nnz; ++i)
+ {
+ fmpz_mod(c, u->entries[i].val, mod);
+ if (fmpz_is_zero(c)) continue;
+ if (u->entries[i].ind != v->entries[j].ind)
+ {
+ flint_printf("Fail: v %%= c has extra/missing entry\n");
+ abort();
+ }
+ if (fmpz_cmp(c, v->entries[j].val) != 0)
+ {
+ flint_printf("Fail: v %%= c has incorrect entry\n");
+ abort();
+ }
+ ++j;
+ }
+ if (j != v->nnz)
+ {
+ flint_printf("Fail: v %%= c has incorrect number of nonzeroes\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_set(v, u, 0);
+ fmpz_sparse_vec_scalar_mods_fmpz(v, v, mod);
+
+ for (i = j = 0; i < u->nnz; ++i)
+ {
+ fmpz_smod(c, u->entries[i].val, mod);
+ if (fmpz_is_zero(c)) continue;
+ if (u->entries[i].ind != v->entries[j].ind)
+ {
+ flint_printf("Fail: v %%s= c has extra/missing entry\n");
+ abort();
+ }
+ if (fmpz_cmp(c, v->entries[j].val) != 0)
+ {
+ flint_printf("Fail: v %%s= c has incorrect entry\n");
+ abort();
+ }
+ ++j;
+ }
+ if (j != v->nnz)
+ {
+ flint_printf("Fail: v %%s= c has incorrect number of nonzeroes\n");
+ abort();
+ }
+
+ fmpz_clear(mod);
+ fmpz_clear(c);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/test/t-scalar_mul.c b/fmpz_sparse_vec/test/t-scalar_mul.c
new file mode 100644
index 0000000000..f4cdc116b0
--- /dev/null
+++ b/fmpz_sparse_vec/test/t-scalar_mul.c
@@ -0,0 +1,115 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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
+#include "flint.h"
+#include "fmpz_sparse_vec.h"
+#include "fmpz_vec.h"
+#include "ulong_extras.h"
+
+int
+main(void)
+{
+ slong rep, bits, len, nnz;
+ fmpz_t c;
+ fmpz_sparse_vec_t u, v, w, x;
+ FLINT_TEST_INIT(state);
+
+ flint_printf("scalar_mul and muladd....");
+ fflush(stdout);
+
+ for (rep = 0; rep < 1000; rep++)
+ {
+ do bits = n_randint(state, 200);
+ while (bits < UWORD(2));
+ len = n_randint(state, 50);
+ nnz = n_randint(state, len+1);
+
+ fmpz_init(c);
+ fmpz_sparse_vec_init(u);
+ fmpz_sparse_vec_init(v);
+ fmpz_sparse_vec_init(w);
+ fmpz_sparse_vec_init(x);
+
+ fmpz_randtest(c, state, bits);
+ fmpz_sparse_vec_randtest(u, state, nnz, len, bits);
+ fmpz_sparse_vec_randtest(v, state, nnz, len, bits);
+
+ fmpz_sparse_vec_scalar_addmul_fmpz(w, u, v, c);
+ fmpz_sparse_vec_scalar_mul_fmpz(x, v, c);
+ fmpz_sparse_vec_add(x, x, u);
+
+ if (!fmpz_sparse_vec_equal(w, x, 0))
+ {
+ flint_printf("FAIL: u + c*v != u + (c*v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_addmul_fmpz(w, u, v, c);
+ fmpz_sparse_vec_scalar_addmul_fmpz(u, u, v, c);
+
+ if (!fmpz_sparse_vec_equal(u, w, 0))
+ {
+ flint_printf("FAIL: u + c*v != (u += c*v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_addmul_fmpz(w, u, v, c);
+ fmpz_sparse_vec_scalar_addmul_fmpz(v, u, v, c);
+
+ if (!fmpz_sparse_vec_equal(v, w, 0))
+ {
+ flint_printf("FAIL: u + c*v != (u += c*v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_submul_fmpz(w, u, v, c);
+ fmpz_sparse_vec_scalar_submul_fmpz(u, u, v, c);
+
+ if (!fmpz_sparse_vec_equal(u, w, 0))
+ {
+ flint_printf("FAIL: u + c*v != (u += c*v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_submul_fmpz(w, u, v, c);
+ fmpz_sparse_vec_scalar_submul_fmpz(v, u, v, c);
+
+ if (!fmpz_sparse_vec_equal(v, w, 0))
+ {
+ flint_printf("FAIL: u + c*v != (u += c*v)\n");
+ abort();
+ }
+
+ fmpz_sparse_vec_scalar_mul_fmpz(x, v, c);
+ fmpz_sparse_vec_scalar_mul_fmpz(v, v, c);
+
+ if (!fmpz_sparse_vec_equal(v, x, 0))
+ {
+ flint_printf("FAIL: c*v != (c *= v)\n");
+ abort();
+ }
+ fmpz_clear(c);
+ fmpz_sparse_vec_clear(u);
+ fmpz_sparse_vec_clear(v);
+ fmpz_sparse_vec_clear(w);
+ fmpz_sparse_vec_clear(x);
+ }
+
+ FLINT_TEST_CLEANUP(state);
+
+ flint_printf("PASS\n");
+ return 0;
+}
diff --git a/fmpz_sparse_vec/window_init.c b/fmpz_sparse_vec/window_init.c
new file mode 100644
index 0000000000..e85950d813
--- /dev/null
+++ b/fmpz_sparse_vec/window_init.c
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e 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_sparse_vec.h"
+
+void fmpz_sparse_vec_window_init(fmpz_sparse_vec_t window, const fmpz_sparse_vec_t vec, slong i1, slong i2)
+{
+ slong start, end;
+ for (start = 0; start < vec->nnz && vec->entries[start].ind < i1; ++start);
+ for (end = vec->nnz; end > 0 && vec->entries[end-1].ind >= i2; --end);
+ window->entries = vec->entries + start;
+ window->nnz = end - start;
+}
diff --git a/fq_mat/addmul.c b/fq_mat/addmul.c
new file mode 100644
index 0000000000..f2c0cf8b8e
--- /dev/null
+++ b/fq_mat/addmul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/addmul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat/mul_vec.c b/fq_mat/mul_vec.c
new file mode 100644
index 0000000000..5927e18dff
--- /dev/null
+++ b/fq_mat/mul_vec.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/mul_vec.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat/scalar_addmul.c b/fq_mat/scalar_addmul.c
new file mode 100644
index 0000000000..7375dbb9a9
--- /dev/null
+++ b/fq_mat/scalar_addmul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/scalar_addmul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat/scalar_mul.c b/fq_mat/scalar_mul.c
new file mode 100644
index 0000000000..ba7f8885d9
--- /dev/null
+++ b/fq_mat/scalar_mul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/scalar_mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat/scalar_submul.c b/fq_mat/scalar_submul.c
new file mode 100644
index 0000000000..6aad2e4767
--- /dev/null
+++ b/fq_mat/scalar_submul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/scalar_submul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat/transpose.c b/fq_mat/transpose.c
new file mode 100644
index 0000000000..4c8f61aef0
--- /dev/null
+++ b/fq_mat/transpose.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_mat_templates/transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_mat_templates.h b/fq_mat_templates.h
index 4454e4966f..718f1be7de 100644
--- a/fq_mat_templates.h
+++ b/fq_mat_templates.h
@@ -274,6 +274,10 @@ FLINT_DLL void TEMPLATE(T, mat_randtriu)(TEMPLATE(T, mat_t) mat, flint_rand_t st
/* Transpose */
+FLINT_DLL void TEMPLATE(T, mat_transpose)(TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, ctx_t) ctx);
+
/* Addition and subtraction */
FLINT_DLL void TEMPLATE(T, mat_add)(TEMPLATE(T, mat_t) C,
@@ -290,6 +294,12 @@ FLINT_DLL void TEMPLATE(T, mat_neg)(TEMPLATE(T, mat_t) B,
const TEMPLATE(T, mat_t) A,
const TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL void TEMPLATE(T, mat_addmul)(TEMPLATE(T, mat_t) D,
+ const TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, ctx_t) ctx);
+
FLINT_DLL void TEMPLATE(T, mat_submul)(TEMPLATE(T, mat_t) D,
const TEMPLATE(T, mat_t) C,
const TEMPLATE(T, mat_t) A,
@@ -298,6 +308,24 @@ FLINT_DLL void TEMPLATE(T, mat_submul)(TEMPLATE(T, mat_t) D,
/* Scalar operations */
+FLINT_DLL void TEMPLATE(T, mat_scalar_mul)(TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL void TEMPLATE(T, mat_scalar_addmul)(TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL void TEMPLATE(T, mat_scalar_submul)(TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx);
+
+
/* Multiplication */
FLINT_DLL void TEMPLATE(T, mat_mul)(TEMPLATE(T, mat_t) C,
@@ -305,6 +333,11 @@ FLINT_DLL void TEMPLATE(T, mat_mul)(TEMPLATE(T, mat_t) C,
const TEMPLATE(T, mat_t) B,
const TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL void TEMPLATE(T, mat_mul_vec)(TEMPLATE(T, struct) *y,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, struct) *x,
+ const TEMPLATE(T, ctx_t) ctx);
+
FLINT_DLL void TEMPLATE(T, mat_mul_classical)(TEMPLATE(T, mat_t) C,
const TEMPLATE(T, mat_t) A,
const TEMPLATE(T, mat_t) B,
diff --git a/fq_mat_templates/addmul.c b/fq_mat_templates/addmul.c
new file mode 100644
index 0000000000..509a15d34e
--- /dev/null
+++ b/fq_mat_templates/addmul.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_addmul) (TEMPLATE(T, mat_t) D,
+ const TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, ctx_t) ctx)
+{
+ TEMPLATE(T, mat_t) tmp;
+ TEMPLATE(T, mat_init) (tmp, A->r, B->c, ctx);
+ TEMPLATE(T, mat_mul) (tmp, A, B, ctx);
+ TEMPLATE(T, mat_add) (D, C, tmp, ctx);
+ TEMPLATE(T, mat_clear) (tmp, ctx);
+}
+
+
+#endif
diff --git a/fq_mat_templates/mul_vec.c b/fq_mat_templates/mul_vec.c
new file mode 100644
index 0000000000..1bc400f9e2
--- /dev/null
+++ b/fq_mat_templates/mul_vec.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_mul_vec) (TEMPLATE(T, struct) *y,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, struct) *x, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < A->r; ++i)
+ _TEMPLATE(T, vec_dot) (&y[i], A->rows[i], x, A->c, ctx);
+}
+
+
+#endif
diff --git a/fq_mat_templates/scalar_addmul.c b/fq_mat_templates/scalar_addmul.c
new file mode 100644
index 0000000000..d4b1b03e7b
--- /dev/null
+++ b/fq_mat_templates/scalar_addmul.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_scalar_addmul) (TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < C->r; i++)
+ {
+ _TEMPLATE(T, vec_set) (C->rows[i], A->rows[i], A->c, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (C->rows[i], B->rows[i], A->c, c, ctx);
+ }
+}
+
+
+#endif
diff --git a/fq_mat_templates/scalar_mul.c b/fq_mat_templates/scalar_mul.c
new file mode 100644
index 0000000000..a82d79899f
--- /dev/null
+++ b/fq_mat_templates/scalar_mul.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_scalar_mul) (TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < B->r; i++)
+ {
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (B->rows[i], A->rows[i], A->c, c, ctx);
+ }
+}
+
+
+#endif
diff --git a/fq_mat_templates/scalar_submul.c b/fq_mat_templates/scalar_submul.c
new file mode 100644
index 0000000000..86765edb3b
--- /dev/null
+++ b/fq_mat_templates/scalar_submul.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_scalar_submul) (TEMPLATE(T, mat_t) C,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, t) c,
+ const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < C->r; i++)
+ {
+ _TEMPLATE(T, vec_set) (C->rows[i], A->rows[i], A->c, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_submul, T)) (C->rows[i], B->rows[i], A->c, c, ctx);
+ }
+}
+
+
+#endif
diff --git a/fq_mat_templates/transpose.c b/fq_mat_templates/transpose.c
new file mode 100644
index 0000000000..9949348e23
--- /dev/null
+++ b/fq_mat_templates/transpose.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2013 Mike Hansen
+
+ 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 .
+*/
+
+#ifdef T
+
+#include "templates.h"
+
+void
+TEMPLATE(T, mat_transpose) (TEMPLATE(T, mat_t) B,
+ const TEMPLATE(T, mat_t) A,
+ const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, j;
+ TEMPLATE(T, t) tmp;
+
+ if (A == B)
+ {
+ TEMPLATE(T, init) (tmp, ctx);
+ for (i = 0; i < A->r; ++i)
+ {
+ for (j = i+1; j < A->c; ++j)
+ {
+ TEMPLATE(T, set) (tmp, &A->rows[i][j], ctx);
+ TEMPLATE(T, set) (&B->rows[i][j], &A->rows[j][i], ctx);
+ TEMPLATE(T, set) (&B->rows[j][i], tmp, ctx);
+ }
+ }
+ TEMPLATE(T, clear) (tmp, ctx);
+ }
+ else
+ {
+ for (i = 0; i < A->r; ++i)
+ {
+ for (j = 0; j < A->c; ++j)
+ {
+ TEMPLATE(T, set) (&B->rows[j][i], &A->rows[i][j], ctx);
+ }
+ }
+ }
+
+}
+
+
+#endif
diff --git a/fq_nmod_mat/addmul.c b/fq_nmod_mat/addmul.c
new file mode 100644
index 0000000000..73dccd5e1f
--- /dev/null
+++ b/fq_nmod_mat/addmul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/addmul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_mat/mul_vec.c b/fq_nmod_mat/mul_vec.c
new file mode 100644
index 0000000000..4ab0ac9c23
--- /dev/null
+++ b/fq_nmod_mat/mul_vec.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/mul_vec.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_mat/scalar_addmul.c b/fq_nmod_mat/scalar_addmul.c
new file mode 100644
index 0000000000..3f5de0d064
--- /dev/null
+++ b/fq_nmod_mat/scalar_addmul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/scalar_addmul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_mat/scalar_mul.c b/fq_nmod_mat/scalar_mul.c
new file mode 100644
index 0000000000..e7c41730a7
--- /dev/null
+++ b/fq_nmod_mat/scalar_mul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/scalar_mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_mat/scalar_submul.c b/fq_nmod_mat/scalar_submul.c
new file mode 100644
index 0000000000..e98442c63e
--- /dev/null
+++ b/fq_nmod_mat/scalar_submul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/scalar_submul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_mat/transpose.c b/fq_nmod_mat/transpose.c
new file mode 100644
index 0000000000..e50dc6f89a
--- /dev/null
+++ b/fq_nmod_mat/transpose.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+
+ 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 "fq_nmod_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_mat_templates/transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat.h b/fq_nmod_sparse_mat.h
new file mode 100644
index 0000000000..c84f117223
--- /dev/null
+++ b/fq_nmod_sparse_mat.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifndef FQ_NMOD_SPARSE_MAT_H
+#define FQ_NMOD_SPARSE_MAT_H
+
+#ifdef FQ_NMOD_SPARSE_MAT_INLINES_C
+#define FQ_SPARSE_MAT_TEMPLATES_INLINE FLINT_DLL
+#define FQ_NMOD_SPARSE_MAT_INLINE FLINT_DLL
+#else
+#define FQ_SPARSE_MAT_TEMPLATES_INLINE static __inline__
+#define FQ_NMOD_SPARSE_MAT_INLINE static __inline__
+#endif
+
+
+#include "fq_nmod.h"
+#include "fq_nmod_vec.h"
+#include "fq_nmod_sparse_vec.h"
+#include "fq_nmod_mat.h"
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates.h"
+#undef CAP_T
+#undef T
+
+#endif
diff --git a/fq_nmod_sparse_mat/from_entries.c b/fq_nmod_sparse_mat/from_entries.c
new file mode 100644
index 0000000000..ca0181ed26
--- /dev/null
+++ b/fq_nmod_sparse_mat/from_entries.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/from_entries.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/inv.c b/fq_nmod_sparse_mat/inv.c
new file mode 100644
index 0000000000..eb17ddb966
--- /dev/null
+++ b/fq_nmod_sparse_mat/inv.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/inv.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/lu.c b/fq_nmod_sparse_mat/lu.c
new file mode 100644
index 0000000000..b1dc772bed
--- /dev/null
+++ b/fq_nmod_sparse_mat/lu.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_block_lanczos.c b/fq_nmod_sparse_mat/nullspace_block_lanczos.c
new file mode 100644
index 0000000000..5b4e5416f7
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_block_lanczos.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_block_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_block_wiedemann.c b/fq_nmod_sparse_mat/nullspace_block_wiedemann.c
new file mode 100644
index 0000000000..a1a1e2d597
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_block_wiedemann.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_block_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_lanczos.c b/fq_nmod_sparse_mat/nullspace_lanczos.c
new file mode 100644
index 0000000000..2d792bfb05
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_lanczos.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_lu.c b/fq_nmod_sparse_mat/nullspace_lu.c
new file mode 100644
index 0000000000..e2ebf8770c
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_lu.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_rref.c b/fq_nmod_sparse_mat/nullspace_rref.c
new file mode 100644
index 0000000000..1479456f60
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_rref.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/nullspace_wiedemann.c b/fq_nmod_sparse_mat/nullspace_wiedemann.c
new file mode 100644
index 0000000000..eb92c2dd6e
--- /dev/null
+++ b/fq_nmod_sparse_mat/nullspace_wiedemann.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/nullspace_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/print_pretty.c b/fq_nmod_sparse_mat/print_pretty.c
new file mode 100644
index 0000000000..1850340b4f
--- /dev/null
+++ b/fq_nmod_sparse_mat/print_pretty.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/print_pretty.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/randtest.c b/fq_nmod_sparse_mat/randtest.c
new file mode 100644
index 0000000000..4faa708f4f
--- /dev/null
+++ b/fq_nmod_sparse_mat/randtest.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/randtest.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/rref.c b/fq_nmod_sparse_mat/rref.c
new file mode 100644
index 0000000000..d2124c45bd
--- /dev/null
+++ b/fq_nmod_sparse_mat/rref.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_block_lanczos.c b/fq_nmod_sparse_mat/solve_block_lanczos.c
new file mode 100644
index 0000000000..57b0424fe9
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_block_lanczos.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2020 Kartik Venkatram
+
+ Algorithm taken from P. Montgomery, "A Block Lanczos Algorithm for
+ Finding Dependencies over GF(2)", Advances in Cryptology - EUROCRYPT '95
+
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_block_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_block_wiedemann.c b/fq_nmod_sparse_mat/solve_block_wiedemann.c
new file mode 100644
index 0000000000..fdf88b21dc
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_block_wiedemann.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_block_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_lanczos.c b/fq_nmod_sparse_mat/solve_lanczos.c
new file mode 100644
index 0000000000..5f55f50b77
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_lanczos.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_lu.c b/fq_nmod_sparse_mat/solve_lu.c
new file mode 100644
index 0000000000..310d98b1c1
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_lu.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_rref.c b/fq_nmod_sparse_mat/solve_rref.c
new file mode 100644
index 0000000000..fc7f6deb37
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_rref.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/solve_wiedemann.c b/fq_nmod_sparse_mat/solve_wiedemann.c
new file mode 100644
index 0000000000..3b732c902f
--- /dev/null
+++ b/fq_nmod_sparse_mat/solve_wiedemann.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/solve_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-add.c b/fq_nmod_sparse_mat/test/t-add.c
new file mode 100644
index 0000000000..3e96ab4964
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-add.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-add.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-concat_horizontal.c b/fq_nmod_sparse_mat/test/t-concat_horizontal.c
new file mode 100644
index 0000000000..e4b7afdb3e
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-concat_horizontal.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-concat_horizontal.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-concat_vertical.c b/fq_nmod_sparse_mat/test/t-concat_vertical.c
new file mode 100644
index 0000000000..ed52ddd2d1
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-concat_vertical.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-concat_vertical.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-construct.c b/fq_nmod_sparse_mat/test/t-construct.c
new file mode 100644
index 0000000000..b8973155e9
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-construct.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-construct.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-dense.c b/fq_nmod_sparse_mat/test/t-dense.c
new file mode 100644
index 0000000000..da27a9ab68
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-dense.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-dense.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-init_clear.c b/fq_nmod_sparse_mat/test/t-init_clear.c
new file mode 100644
index 0000000000..b40e950776
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-init_clear.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-init_clear.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-inv.c b/fq_nmod_sparse_mat/test/t-inv.c
new file mode 100644
index 0000000000..36d3648729
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-inv.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-inv.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-lu.c b/fq_nmod_sparse_mat/test/t-lu.c
new file mode 100644
index 0000000000..7e4e75a6e8
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-lu.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-mul.c b/fq_nmod_sparse_mat/test/t-mul.c
new file mode 100644
index 0000000000..654c1039fa
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-mul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-neg.c b/fq_nmod_sparse_mat/test/t-neg.c
new file mode 100644
index 0000000000..45b024b361
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-neg.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-neg.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-nullspace.c b/fq_nmod_sparse_mat/test/t-nullspace.c
new file mode 100644
index 0000000000..d561349e5c
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-nullspace.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-nullspace.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-rref.c b/fq_nmod_sparse_mat/test/t-rref.c
new file mode 100644
index 0000000000..ba4aa88694
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-rref.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-scalar_mul.c b/fq_nmod_sparse_mat/test/t-scalar_mul.c
new file mode 100644
index 0000000000..7c704d8f2e
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-scalar_mul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-scalar_mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-solve.c b/fq_nmod_sparse_mat/test/t-solve.c
new file mode 100644
index 0000000000..5e8763aaee
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-solve.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-solve.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/test/t-transpose.c b/fq_nmod_sparse_mat/test/t-transpose.c
new file mode 100644
index 0000000000..1392eca38a
--- /dev/null
+++ b/fq_nmod_sparse_mat/test/t-transpose.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/test/t-transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/transpose.c b/fq_nmod_sparse_mat/transpose.c
new file mode 100644
index 0000000000..7db5f5b7c9
--- /dev/null
+++ b/fq_nmod_sparse_mat/transpose.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_mat/window_init.c b/fq_nmod_sparse_mat/window_init.c
new file mode 100644
index 0000000000..bd8c3f34e7
--- /dev/null
+++ b/fq_nmod_sparse_mat/window_init.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_nmod_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_mat_templates/window_init.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec.h b/fq_nmod_sparse_vec.h
new file mode 100644
index 0000000000..a6037b3489
--- /dev/null
+++ b/fq_nmod_sparse_vec.h
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifndef FQ_NMOD_SPARSE_VEC_H
+#define FQ_NMOD_SPARSE_VEC_H
+
+#ifdef FQ_NMOD_SPARSE_VEC_INLINES_C
+#define FQ_SPARSE_VEC_TEMPLATES_INLINE FLINT_DLL
+#define FQ_NMOD_SPARSE_VEC_INLINE FLINT_DLL
+#else
+#define FQ_SPARSE_VEC_TEMPLATES_INLINE static __inline__
+#define FQ_NMOD_SPARSE_VEC_INLINE static __inline__
+#endif
+
+#include "fq_nmod.h"
+#include "fq_nmod_vec.h"
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates.h"
+#undef CAP_T
+#undef T
+
+#endif
diff --git a/fq_nmod_sparse_vec/add.c b/fq_nmod_sparse_vec/add.c
new file mode 100644
index 0000000000..0b2952f536
--- /dev/null
+++ b/fq_nmod_sparse_vec/add.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/add.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/print_pretty.c b/fq_nmod_sparse_vec/print_pretty.c
new file mode 100644
index 0000000000..770eb351d5
--- /dev/null
+++ b/fq_nmod_sparse_vec/print_pretty.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/print_pretty.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/randtest.c b/fq_nmod_sparse_vec/randtest.c
new file mode 100644
index 0000000000..17b968995b
--- /dev/null
+++ b/fq_nmod_sparse_vec/randtest.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/randtest.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/scalar_addmul.c b/fq_nmod_sparse_vec/scalar_addmul.c
new file mode 100644
index 0000000000..096833c1a8
--- /dev/null
+++ b/fq_nmod_sparse_vec/scalar_addmul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/scalar_addmul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/scalar_submul.c b/fq_nmod_sparse_vec/scalar_submul.c
new file mode 100644
index 0000000000..1a2f5dc482
--- /dev/null
+++ b/fq_nmod_sparse_vec/scalar_submul.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/scalar_submul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/sub.c b/fq_nmod_sparse_vec/sub.c
new file mode 100644
index 0000000000..1ad014b2be
--- /dev/null
+++ b/fq_nmod_sparse_vec/sub.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/sub.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-add.c b/fq_nmod_sparse_vec/test/t-add.c
new file mode 100644
index 0000000000..01a6e60300
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-add.c
@@ -0,0 +1,21 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-add.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-concat.c b/fq_nmod_sparse_vec/test/t-concat.c
new file mode 100644
index 0000000000..8253306aec
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-concat.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-concat.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-construct.c b/fq_nmod_sparse_vec/test/t-construct.c
new file mode 100644
index 0000000000..40e309f7ef
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-construct.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-construct.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-dense.c b/fq_nmod_sparse_vec/test/t-dense.c
new file mode 100644
index 0000000000..07fa2b09e5
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-dense.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-dense.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-dot.c b/fq_nmod_sparse_vec/test/t-dot.c
new file mode 100644
index 0000000000..45d119f1f3
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-dot.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-dot.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-init_clear.c b/fq_nmod_sparse_vec/test/t-init_clear.c
new file mode 100644
index 0000000000..607fa4e215
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-init_clear.c
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-init_clear.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-neg.c b/fq_nmod_sparse_vec/test/t-neg.c
new file mode 100644
index 0000000000..b62369e249
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-neg.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-neg.c"
+#undef CAP_T
+#undef T
diff --git a/fq_nmod_sparse_vec/test/t-scalar_mul.c b/fq_nmod_sparse_vec/test/t-scalar_mul.c
new file mode 100644
index 0000000000..6a7ea91da8
--- /dev/null
+++ b/fq_nmod_sparse_vec/test/t-scalar_mul.c
@@ -0,0 +1,23 @@
+/*
+ wopyright (w) 2011 Fredrik Johansson
+
+ 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 "fq_nmod_sparse_vec.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq_nmod
+#define CAP_T FQ_NMOD
+#include "fq_sparse_vec_templates/test/t-scalar_mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat.h b/fq_sparse_mat.h
new file mode 100644
index 0000000000..d1e2c6411b
--- /dev/null
+++ b/fq_sparse_mat.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2013 Mike Hansen
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifndef FQ_SPARSE_MAT_H
+#define FQ_SPARSE_MAT_H
+
+#ifdef FQ_SPARSE_MAT_INLINES_C
+#define FQ_SPARSE_MAT_TEMPLATES_INLINE FLINT_DLL
+#define FQ_SPARSE_MAT_INLINE FLINT_DLL
+#else
+#define FQ_SPARSE_MAT_TEMPLATES_INLINE static __inline__
+#define FQ_SPARSE_MAT_INLINE static __inline__
+#endif
+
+
+#include "fq.h"
+#include "fq_vec.h"
+#include "fq_sparse_vec.h"
+#include "fq_mat.h"
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates.h"
+#undef CAP_T
+#undef T
+
+#endif
diff --git a/fq_sparse_mat/from_entries.c b/fq_sparse_mat/from_entries.c
new file mode 100644
index 0000000000..24ea5e68ef
--- /dev/null
+++ b/fq_sparse_mat/from_entries.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/from_entries.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/inv.c b/fq_sparse_mat/inv.c
new file mode 100644
index 0000000000..422d1a27ee
--- /dev/null
+++ b/fq_sparse_mat/inv.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/inv.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/lu.c b/fq_sparse_mat/lu.c
new file mode 100644
index 0000000000..8b4a2fec4a
--- /dev/null
+++ b/fq_sparse_mat/lu.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_block_lanczos.c b/fq_sparse_mat/nullspace_block_lanczos.c
new file mode 100644
index 0000000000..d806b0a350
--- /dev/null
+++ b/fq_sparse_mat/nullspace_block_lanczos.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_block_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_block_wiedemann.c b/fq_sparse_mat/nullspace_block_wiedemann.c
new file mode 100644
index 0000000000..54e56c2912
--- /dev/null
+++ b/fq_sparse_mat/nullspace_block_wiedemann.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_block_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_lanczos.c b/fq_sparse_mat/nullspace_lanczos.c
new file mode 100644
index 0000000000..cc2a572ef1
--- /dev/null
+++ b/fq_sparse_mat/nullspace_lanczos.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_lu.c b/fq_sparse_mat/nullspace_lu.c
new file mode 100644
index 0000000000..81f0dc4b54
--- /dev/null
+++ b/fq_sparse_mat/nullspace_lu.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_rref.c b/fq_sparse_mat/nullspace_rref.c
new file mode 100644
index 0000000000..f0f1b8d643
--- /dev/null
+++ b/fq_sparse_mat/nullspace_rref.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/nullspace_wiedemann.c b/fq_sparse_mat/nullspace_wiedemann.c
new file mode 100644
index 0000000000..32a1393376
--- /dev/null
+++ b/fq_sparse_mat/nullspace_wiedemann.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/nullspace_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/print_pretty.c b/fq_sparse_mat/print_pretty.c
new file mode 100644
index 0000000000..8090dcc69d
--- /dev/null
+++ b/fq_sparse_mat/print_pretty.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/print_pretty.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/randtest.c b/fq_sparse_mat/randtest.c
new file mode 100644
index 0000000000..5ffb7f0cf4
--- /dev/null
+++ b/fq_sparse_mat/randtest.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/randtest.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/rref.c b/fq_sparse_mat/rref.c
new file mode 100644
index 0000000000..a5db83126a
--- /dev/null
+++ b/fq_sparse_mat/rref.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_block_lanczos.c b/fq_sparse_mat/solve_block_lanczos.c
new file mode 100644
index 0000000000..30206162b3
--- /dev/null
+++ b/fq_sparse_mat/solve_block_lanczos.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ Algorithm taken from P. Montgomery, "A Block Lanczos Algorithm for
+ Finding Dependencies over GF(2)", Advances in Cryptology - EUROCRYPT '95
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_block_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_block_wiedemann.c b/fq_sparse_mat/solve_block_wiedemann.c
new file mode 100644
index 0000000000..208a05dd92
--- /dev/null
+++ b/fq_sparse_mat/solve_block_wiedemann.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_block_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_lanczos.c b/fq_sparse_mat/solve_lanczos.c
new file mode 100644
index 0000000000..e9ffdb4c09
--- /dev/null
+++ b/fq_sparse_mat/solve_lanczos.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_lanczos.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_lu.c b/fq_sparse_mat/solve_lu.c
new file mode 100644
index 0000000000..587186cf8f
--- /dev/null
+++ b/fq_sparse_mat/solve_lu.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_rref.c b/fq_sparse_mat/solve_rref.c
new file mode 100644
index 0000000000..9a8cd0257b
--- /dev/null
+++ b/fq_sparse_mat/solve_rref.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/solve_wiedemann.c b/fq_sparse_mat/solve_wiedemann.c
new file mode 100644
index 0000000000..c40f0f79af
--- /dev/null
+++ b/fq_sparse_mat/solve_wiedemann.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/solve_wiedemann.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-add.c b/fq_sparse_mat/test/t-add.c
new file mode 100644
index 0000000000..c5e5c1590d
--- /dev/null
+++ b/fq_sparse_mat/test/t-add.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-add.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-concat_horizontal.c b/fq_sparse_mat/test/t-concat_horizontal.c
new file mode 100644
index 0000000000..a44d374233
--- /dev/null
+++ b/fq_sparse_mat/test/t-concat_horizontal.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-concat_horizontal.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-concat_vertical.c b/fq_sparse_mat/test/t-concat_vertical.c
new file mode 100644
index 0000000000..ee3b99ca6f
--- /dev/null
+++ b/fq_sparse_mat/test/t-concat_vertical.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2015 Elena Sergeicheva
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-concat_vertical.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-construct.c b/fq_sparse_mat/test/t-construct.c
new file mode 100644
index 0000000000..b15335ce2c
--- /dev/null
+++ b/fq_sparse_mat/test/t-construct.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-construct.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-dense.c b/fq_sparse_mat/test/t-dense.c
new file mode 100644
index 0000000000..c9207725d2
--- /dev/null
+++ b/fq_sparse_mat/test/t-dense.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-dense.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-init_clear.c b/fq_sparse_mat/test/t-init_clear.c
new file mode 100644
index 0000000000..ec140aa821
--- /dev/null
+++ b/fq_sparse_mat/test/t-init_clear.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-init_clear.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-inv.c b/fq_sparse_mat/test/t-inv.c
new file mode 100644
index 0000000000..a4d51256ba
--- /dev/null
+++ b/fq_sparse_mat/test/t-inv.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-inv.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-lu.c b/fq_sparse_mat/test/t-lu.c
new file mode 100644
index 0000000000..f50b51f280
--- /dev/null
+++ b/fq_sparse_mat/test/t-lu.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-lu.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-mul.c b/fq_sparse_mat/test/t-mul.c
new file mode 100644
index 0000000000..0df33448d9
--- /dev/null
+++ b/fq_sparse_mat/test/t-mul.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-neg.c b/fq_sparse_mat/test/t-neg.c
new file mode 100644
index 0000000000..99cd1b8ad1
--- /dev/null
+++ b/fq_sparse_mat/test/t-neg.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-neg.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-nullspace.c b/fq_sparse_mat/test/t-nullspace.c
new file mode 100644
index 0000000000..0703f1d579
--- /dev/null
+++ b/fq_sparse_mat/test/t-nullspace.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-nullspace.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-rref.c b/fq_sparse_mat/test/t-rref.c
new file mode 100644
index 0000000000..00eb87f61c
--- /dev/null
+++ b/fq_sparse_mat/test/t-rref.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-rref.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-scalar_mul.c b/fq_sparse_mat/test/t-scalar_mul.c
new file mode 100644
index 0000000000..0bbd83d529
--- /dev/null
+++ b/fq_sparse_mat/test/t-scalar_mul.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-scalar_mul.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-solve.c b/fq_sparse_mat/test/t-solve.c
new file mode 100644
index 0000000000..d0c160e64c
--- /dev/null
+++ b/fq_sparse_mat/test/t-solve.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-solve.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/test/t-transpose.c b/fq_sparse_mat/test/t-transpose.c
new file mode 100644
index 0000000000..7adcfd3374
--- /dev/null
+++ b/fq_sparse_mat/test/t-transpose.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/test/t-transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/transpose.c b/fq_sparse_mat/transpose.c
new file mode 100644
index 0000000000..c64bd3c1ac
--- /dev/null
+++ b/fq_sparse_mat/transpose.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/transpose.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat/window_init.c b/fq_sparse_mat/window_init.c
new file mode 100644
index 0000000000..ec6fa02e2a
--- /dev/null
+++ b/fq_sparse_mat/window_init.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 "fq_sparse_mat.h"
+
+#ifdef T
+#undef T
+#endif
+
+#define T fq
+#define CAP_T FQ
+#include "fq_sparse_mat_templates/window_init.c"
+#undef CAP_T
+#undef T
diff --git a/fq_sparse_mat_templates.h b/fq_sparse_mat_templates.h
new file mode 100644
index 0000000000..7d147edbac
--- /dev/null
+++ b/fq_sparse_mat_templates.h
@@ -0,0 +1,415 @@
+/*
+ Copyright (C) 2010 William Hart
+ Copyright (C) 2010,2011 Fredrik Johansson
+ Copyright (C) 2014 Ashish Kedia
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+#ifdef T
+
+#include
+#include "flint.h"
+#include "longlong.h"
+#include "templates.h"
+#include "ulong_extras.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* A sparse matrix is a list of sparse vectors */
+typedef struct
+{
+ TEMPLATE(T, sparse_vec_struct) *rows;
+ slong r;
+ slong c;
+ slong c_off;
+}
+TEMPLATE(T, sparse_mat_struct);
+
+typedef TEMPLATE(T, sparse_mat_struct) TEMPLATE(T, sparse_mat_t)[1];
+
+/* Memory management */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_init) (TEMPLATE(T, sparse_mat_t) M, slong rows, slong cols, const TEMPLATE(T, ctx_t) ctx)
+{
+ FLINT_ASSERT(rows >= 0 && cols >= 0);
+ M->rows = flint_calloc(rows, sizeof(*M->rows));
+ M->r = rows;
+ M->c = cols;
+ M->c_off = 0;
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_clear) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) TEMPLATE(T, sparse_vec_clear)(&M->rows[i], ctx);
+ flint_free(M->rows);
+ memset(M, 0, sizeof(*M));
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_resize) (TEMPLATE(T, sparse_mat_t) M, slong rows, slong cols, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(rows >= 0 && cols >= 0);
+ if (M->r != rows) {
+ if (M->r > rows)
+ {
+ for (i = rows; i < M->r; ++i)
+ {
+ TEMPLATE(T, sparse_vec_clear)(&M->rows[i], ctx);
+ }
+ }
+ M->rows = flint_realloc(M->rows, rows*sizeof(*M->rows));
+ if (M->r < rows)
+ {
+ for (i = M->r; i < rows; ++i)
+ {
+ TEMPLATE(T, sparse_vec_init)(&M->rows[i], ctx);
+ }
+ }
+ M->r = rows;
+ }
+ if (cols < M->c)
+ {
+ for (i = 0; i < M->r; ++i)
+ {
+ TEMPLATE(T, sparse_vec_resize)(&M->rows[i], cols, ctx);
+ }
+ }
+ M->c = cols;
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_swap) (TEMPLATE(T, sparse_mat_t) M1, TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, ctx_t) ctx)
+{
+ TEMPLATE(T, sparse_mat_t) tmp;
+ *tmp = *M1; *M1 = *M2; *M2 = *tmp;
+}
+
+/* One-time instantiation */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_zero) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) TEMPLATE(T, sparse_vec_zero)(&M->rows[i], ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_one) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) TEMPLATE(T, sparse_vec_one)(&M->rows[i], i, ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_set) (TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, rmax = FLINT_MIN(M->r, M->r);
+ if(M==N) return;
+ for(i=0; irows[i], &M->rows[i], M->c_off, ctx);
+}
+
+FLINT_DLL
+void TEMPLATE(T, sparse_mat_from_entries)(TEMPLATE(T, sparse_mat_t) M, slong * rows, slong * cols, TEMPLATE(T, struct) * vals, slong nnz, const TEMPLATE(T, ctx_t) ctx);
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_append_col) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *v, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for(i=0; ir; ++i) TEMPLATE(T, sparse_vec_set_entry)(&M->rows[i], M->c, &v[i], ctx);
+ M->c += 1;
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_append_row) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, sparse_vec_t) v, const TEMPLATE(T, ctx_t) ctx)
+{
+ M->rows = realloc(M->rows, (M->r+1)*sizeof(*M->rows));
+ memset(M->rows + M->r, 0, sizeof(*M->rows));
+ TEMPLATE(T, sparse_vec_set)(&M->rows[M->r], v, 0, ctx);
+ M->r += 1;
+}
+
+/* Convert from/to dense matrix */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_from_dense) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, mat_t) dM, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, rmax = FLINT_MIN(M->r, dM->r);
+ for (i = 0; i < rmax; ++i) TEMPLATE(T, sparse_vec_from_dense)(&M->rows[i], dM->rows[i], dM->c, ctx);
+}
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_to_dense) (TEMPLATE(T, mat_t) dM, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, rmax = FLINT_MIN(M->r, dM->r);
+ for (i = 0; i < rmax; ++i) TEMPLATE(T, sparse_vec_to_dense)(dM->rows[i], &M->rows[i], dM->c, ctx);
+}
+
+/* Windows, concatenation, and splitting */
+FLINT_DLL
+void TEMPLATE(T, sparse_mat_window_init) (TEMPLATE(T, sparse_mat_t) W, const TEMPLATE(T, sparse_mat_t) M, slong r1, slong c1, slong r2, slong c2, const TEMPLATE(T, ctx_t) ctx);
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_window_clear) (TEMPLATE(T, sparse_mat_t) W, TEMPLATE(T, ctx_t) ctx)
+{
+ flint_free(W->rows);
+ memset(W, 0, sizeof(*W));
+}
+
+
+/* Combine M1 and M2 into block matrix B = [M1 M2] */
+/* B->r must equal M1->r and M2->r */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_concat_horizontal)(TEMPLATE(T, sparse_mat_t) B,
+ const TEMPLATE(T, sparse_mat_t) M1, const TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ B->c = M1->c + M2->c;
+ for (i = 0; i < B->r; ++i)
+ TEMPLATE(T, sparse_vec_concat)(&B->rows[i], &M1->rows[i], &M2->rows[i], M1->c, ctx);
+}
+/* Combine M1 and M2 into block matrix B = [M1^t M1^t]^t */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_concat_vertical) (TEMPLATE(T, sparse_mat_t) B, const TEMPLATE(T, sparse_mat_t) M1, const TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ B->c = FLINT_MAX(M1->c, M2->c);
+ for (i = 0; i < M1->r; ++i)
+ TEMPLATE(T, sparse_vec_set)(&B->rows[i], &M1->rows[i], M1->c_off, ctx);
+ for (i = M1->r; i < B->r; ++i)
+ TEMPLATE(T, sparse_vec_set)(&B->rows[i], &M2->rows[i-M1->r], M2->c_off, ctx);
+}
+
+/* Split block matrix B = [M1 M2] into submatrices M1 and M2 */
+/* M1->r and M2->r must equal B->r */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_split_horizontal) (TEMPLATE(T, sparse_mat_t) M1, TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, sparse_mat_t) B, slong c, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for(i=0; ir; ++i) TEMPLATE(T, sparse_vec_split)(&M1->rows[i], &M2->rows[i], &B->rows[i], c, ctx);
+}
+
+/* Split block matix B = [M1^t M1^t]^t into submatrices M1 and M2 */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_split_vertical) (TEMPLATE(T, sparse_mat_t) M1, TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, sparse_mat_t) B, slong r, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ r = FLINT_MIN(r, B->r);
+ for(i=0; irows[i], &B->rows[i], B->c_off, ctx);
+ for(i=r; ir; ++i) TEMPLATE(T, sparse_vec_set)(&M2->rows[i-r], &B->rows[i], B->c_off, ctx);
+}
+
+/* Matrix permutation */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_permute_cols)(TEMPLATE(T, sparse_mat_t) M, slong *Q, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) {
+ if(!M->rows[i].nnz) continue;
+ TEMPLATE(T, sparse_vec_permute_inds)(&M->rows[i], Q, ctx);
+ qsort(M->rows[i].entries, M->rows[i].nnz, sizeof(*M->rows[i].entries), TEMPLATE(T, sparse_entry_cmp));
+ }
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_permute_rows)(TEMPLATE(T, sparse_mat_t) M, slong *P, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ TEMPLATE(T, sparse_vec_struct) *prows;
+ prows = flint_calloc(M->r, sizeof(*prows));
+ for (i = 0; i < M->r; ++i) prows[P[i]] = M->rows[i];
+ memcpy(M->rows, prows, M->r*sizeof(*M->rows));
+ flint_free(prows);
+}
+
+/* Random matrix generation */
+FLINT_DLL void TEMPLATE(T, sparse_mat_randtest) (TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong min_nnz, slong max_nnz, const TEMPLATE(T, ctx_t) ctx);
+/*
+FLINT_DLL void TEMPLATE(T, sparse_mat_randfull) (TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL int TEMPLATE(T, sparse_mat_randpermdiag)(TEMPLATE(T, sparse_mat_t) M, flint_rand_t state,
+ const TEMPLATE(T, struct) *diag, slong n);
+FLINT_DLL void TEMPLATE(T, sparse_mat_randrank) (TEMPLATE(T, sparse_mat_t), flint_rand_t state, slong rank, TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL void TEMPLATE(T, sparse_mat_randops) (TEMPLATE(T, sparse_mat_t) M, slong count, flint_rand_t state, TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL void TEMPLATE(T, sparse_mat_randtril) (TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, int unit, TEMPLATE(T, ctx_t) ctx);
+FLINT_DLL void TEMPLATE(T, sparse_mat_randtriu) (TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, int unit, TEMPLATE(T, ctx_t) ctx);
+ */
+
+FLINT_DLL void TEMPLATE(T, sparse_mat_print_pretty) (const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+int TEMPLATE(T, sparse_mat_equal) (const TEMPLATE(T, sparse_mat_t) M1, const TEMPLATE(T, sparse_mat_t) M2, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ if (M1->r != M2->r) return 0;
+ for (i = 0; i < M1->r; ++i)
+ if (!TEMPLATE(T, sparse_vec_equal)(&M1->rows[i], &M2->rows[i], M1->c_off-M2->c_off, ctx)) return 0;
+ return 1;
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+int TEMPLATE(T, sparse_mat_is_zero) (const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i)
+ if (!TEMPLATE(T, sparse_vec_is_zero)(&M->rows[i], ctx)) return 0;
+ return 1;
+}
+
+/* Must have M->r == N->c and M->c == N->r */
+FLINT_DLL void TEMPLATE(T, sparse_mat_transpose) (TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+/* Arithmetic */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_neg) (TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) TEMPLATE(T, sparse_vec_neg)(&N->rows[i], &M->rows[i], ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, TEMPLATE(sparse_mat_scalar_mul, T)) (TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, t) c, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(M->r == N->r);
+ for (i = 0; i < N->r; ++i) TEMPLATE(T, TEMPLATE(sparse_vec_scalar_mul, T))(&N->rows[i], &M->rows[i], c, ctx);
+}
+
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_add) (TEMPLATE(T, sparse_mat_t) O, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) TEMPLATE(T, sparse_vec_add)(&O->rows[i], &M->rows[i], &N->rows[i], ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_sub) (TEMPLATE(T, sparse_mat_t) O, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) TEMPLATE(T, sparse_vec_sub)(&O->rows[i], &M->rows[i], &N->rows[i], ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, TEMPLATE(sparse_mat_scalar_addmul, T)) (TEMPLATE(T, sparse_mat_t) O, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, t) c, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) TEMPLATE(T, TEMPLATE(sparse_vec_scalar_addmul, T))(&O->rows[i], &M->rows[i], &N->rows[i], c, ctx);
+}
+
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, TEMPLATE(sparse_mat_scalar_submul, T)) (TEMPLATE(T, sparse_mat_t) O, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, sparse_mat_t) N, const TEMPLATE(T, t) c, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ FLINT_ASSERT(O->r == M->r && O->r == N->r);
+ for (i = 0; i < O->r; ++i) TEMPLATE(T, TEMPLATE(sparse_vec_scalar_submul, T))(&O->rows[i], &M->rows[i], &N->rows[i], c, ctx);
+}
+
+/* Matrix-vector and matrix-matrix multipliciation */
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_mul_vec) (TEMPLATE(T, struct) *y, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *x, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ for (i = 0; i < M->r; ++i) TEMPLATE(T, sparse_vec_dot_dense)(&y[i], &M->rows[i], x, ctx);
+}
+FQ_SPARSE_MAT_TEMPLATES_INLINE
+void TEMPLATE(T, sparse_mat_mul_mat) (TEMPLATE(T, mat_t) Y, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, mat_t) X, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, j;
+ FLINT_ASSERT(M->r == Y->r && M->c == X->r && X->c == Y->c);
+ TEMPLATE(T, mat_zero) (Y, ctx);
+ for (i = 0; i < M->r; ++i)
+ {
+ for (j = 0; j < M->rows[i].nnz; ++j)
+ {
+ TEMPLATE(T, sparse_entry_struct) *e = &M->rows[i].entries[j];
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T))(Y->rows[i], X->rows[e->ind], X->c, e->val, ctx);
+ }
+ }
+}
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_inv) (TEMPLATE(T, sparse_mat_t) Mi, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+/* Decomposition/reduction */
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_lu)(slong *P, slong *Q, TEMPLATE(T, sparse_mat_t) L, TEMPLATE(T, sparse_mat_t) U, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_rref) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+/* Solve Ax=b */
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_lu) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_rref) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_block_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_solve_block_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+/* Find single nullvector */
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_nullvector_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_nullvector_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_nullvector_block_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+int TEMPLATE(T, sparse_mat_nullvector_block_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx);
+
+/* Note: this should take in uninitialized matrix X */
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_rref) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_lu) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_lanczos) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_wiedemann) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_block_lanczos) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx);
+
+FLINT_DLL
+slong TEMPLATE(T, sparse_mat_nullspace_block_wiedemann) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx);
+
+/* Nullspace */
+/* NMOD_SPARSE_MAT_INLINE
+slong TEMPLATE(T, sparse_mat_nullspace) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, TEMPLATE(T, ctx_t) ctx);
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/fq_sparse_mat_templates/from_entries.c b/fq_sparse_mat_templates/from_entries.c
new file mode 100644
index 0000000000..05d2df253a
--- /dev/null
+++ b/fq_sparse_mat_templates/from_entries.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2011 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+void TEMPLATE(T, sparse_mat_from_entries) (TEMPLATE(T, sparse_mat_t) M, slong * rows, slong * cols, TEMPLATE(T, struct) * vals, slong nnz, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong r, i, j;
+ for (r = i = 0; r < M->r; ++r, i = j)
+ {
+ M->rows[r].nnz = 0;
+ for (j = i; j < nnz && rows[j]==r; ++j);
+ TEMPLATE(T, sparse_vec_from_entries) (&M->rows[r], cols+i, vals+i, j-i, ctx);
+ }
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/inv.c b/fq_sparse_mat_templates/inv.c
new file mode 100644
index 0000000000..9620a2753b
--- /dev/null
+++ b/fq_sparse_mat_templates/inv.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_inv) (TEMPLATE(T, sparse_mat_t) Mi, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong rk;
+ TEMPLATE(T, sparse_mat_t) I, MI;
+
+ /* Create block matrix [M | I] */
+ TEMPLATE(T, sparse_mat_init) (I, M->r, M->r, ctx);
+ TEMPLATE(T, sparse_mat_one) (I, ctx);
+ TEMPLATE(T, sparse_mat_init) (MI, M->r, M->r + M->c, ctx);
+ TEMPLATE(T, sparse_mat_concat_horizontal) (MI, M, I, ctx);
+
+ /* Run Gaussian elimination on first half */
+ MI->c = M->c;
+ rk = TEMPLATE(T, sparse_mat_rref) (MI, ctx);
+ MI->c = M->c+M->r;
+ TEMPLATE(T, sparse_mat_split_horizontal) (I, Mi, MI, M->c, ctx);
+ TEMPLATE(T, sparse_mat_clear) (I, ctx);
+ TEMPLATE(T, sparse_mat_clear) (MI, ctx);
+ return rk;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/lu.c b/fq_sparse_mat_templates/lu.c
new file mode 100644
index 0000000000..091bd775e6
--- /dev/null
+++ b/fq_sparse_mat_templates/lu.c
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+static void heap_up(slong *heap, slong *heap_idx, slong *scores, slong pos)
+{
+ const slong c = heap[pos];
+ slong nc, npos;
+ for (; pos > 0; pos = npos)
+ {
+ npos = (pos-1)/2;
+ nc = heap[npos];
+ if (scores[c] >= scores[nc]) break;
+
+ heap[pos] = nc;
+ heap_idx[nc] = pos;
+ }
+ heap[pos] = c;
+ heap_idx[c] = pos;
+}
+
+static void heap_down(slong *heap, slong *heap_idx, slong *scores, slong size, slong pos)
+{
+ const slong c = heap[pos];
+ slong nc, npos;
+ for (; pos < (size-1)/2; pos = npos)
+ {
+ npos = 2*pos+1;
+ if (npos+1 < size && scores[heap[npos]] > scores[heap[npos+1]]) ++npos;
+ nc = heap[npos];
+ if (scores[c] <= scores[nc]) break;
+
+ heap[pos] = nc;
+ heap_idx[nc] = pos;
+ }
+ heap[pos] = c;
+ heap_idx[c] = pos;
+}
+
+/* static void print_heap(slong *heap, slong *scores, slong size)
+{
+ slong level, i;
+ for (level = 1; level <= size; level<<=1)
+ {
+ for (i = level; i <= size && i < 2*level; ++i)
+ {
+ flint_printf("%wd:%wd,%wd\t", i-1, heap[i-1], scores[heap[i-1]]);
+ }
+ flint_printf("\n");
+ }
+}
+ */
+slong TEMPLATE(T, sparse_mat_lu)(slong *P, slong *Q,
+ TEMPLATE(T, sparse_mat_t) L, TEMPLATE(T, sparse_mat_t) U,
+ const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, j, r, c, pr, pc, rank, remr, remc;
+ slong *heap, *heap_idx, *scores, heap_size;
+ TEMPLATE(T, t) cinv, cc;
+ TEMPLATE(T, sparse_mat_t) Lt;
+ TEMPLATE(T, sparse_vec_struct) *pcol, *prow, *row, *col;
+
+ if (M->r == 0 || M->c == 0)
+ {
+ TEMPLATE(T, sparse_mat_zero) (L, ctx);
+ TEMPLATE(T, sparse_mat_zero) (U, ctx);
+ for (i = 0; i < M->r; ++i) P[i] = i;
+ for (i = 0; i < M->c; ++i) Q[i] = i;
+ return 0;
+ }
+ TEMPLATE(T, init) (cinv, ctx);
+ TEMPLATE(T, init) (cc, ctx);
+ TEMPLATE(T, sparse_mat_init) (Lt, L->c, L->r, ctx);
+ TEMPLATE(T, sparse_mat_transpose) (Lt, M, ctx);
+ TEMPLATE(T, sparse_mat_set) (U, M, ctx);
+
+ /* Set up permutations */
+ remr = M->r, remc = M->c;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!U->rows[r].nnz) P[r] = --remr;
+ else P[r] = -1;
+ }
+ for (c = 0; c < M->c; ++c)
+ {
+ if (!Lt->rows[c].nnz) Q[c] = --remc;
+ else Q[c] = -1;
+ }
+
+ /* Make heap of nonzero columns by size */
+ heap_size = M->c;
+ heap = flint_malloc(M->c*sizeof(*heap));
+ scores = flint_malloc(M->c*sizeof(*scores));
+ heap_idx = flint_malloc(M->c*sizeof(*heap_idx));
+ for (i = 0; i < M->c; ++i)
+ {
+ scores[i] = Lt->rows[i].nnz; /* TODO: randomized tiebreaker */
+ heap[i] = i;
+ heap_up(heap, heap_idx, scores, i);
+ }
+ /* Run elimination */
+ rank = 0;
+ for (heap_size = M->c; heap_size > 0; )
+ {
+ /* Get lowest weight column (top of heap) */
+ pc = heap[0];
+ pcol = &Lt->rows[pc];
+ heap[0] = heap[--heap_size];
+ heap_down(heap, heap_idx, scores, heap_size, 0);
+ if (pcol->nnz == 0) continue; /* Empty columns already dealt with */
+ Q[pc] = rank; /* Move pivot column to front */
+
+ /* Get lowest weight incident row */
+ pr = pcol->entries[0].ind, prow = &U->rows[pr];
+ for (j = 1; j < pcol->nnz; ++j)
+ {
+ r = pcol->entries[j].ind, row = &U->rows[r];
+ if (row->nnz < prow->nnz) pr = r, prow = row;
+ }
+ P[pr] = rank; /* Move pivot row to front */
+
+ /* Invert pivot */
+ TEMPLATE(T, inv) (cinv, *TEMPLATE(T, sparse_vec_at) (prow, pc, ctx), ctx);
+
+ /* Gaussian eliminate rows */
+ for (j = 0; j < pcol->nnz; ++j)
+ {
+ r = pcol->entries[j].ind, row = &U->rows[r];
+ if (P[r] >= 0) continue; /* Skip previous pivot rows */
+ TEMPLATE(T, mul) (cc, cinv, *TEMPLATE(T, sparse_vec_at) (row, pc, ctx), ctx);
+ TEMPLATE(T, neg) (cc, cc, ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_addmul, T)) (row, row, prow, cc, ctx);
+ if (row->nnz == 0) P[r] = --remr;
+ }
+ /* Gaussian eliminate cols */
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_mul, T)) (pcol, pcol, cinv, ctx);
+ for (j = 0; j < prow->nnz; ++j)
+ {
+ c = prow->entries[j].ind, col = &Lt->rows[c];
+ if (Q[c] >= 0) continue; /* Skip previous pivot columns */
+ TEMPLATE(T, neg) (cc, *TEMPLATE(T, sparse_vec_at) (col, pr, ctx), ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_addmul, T)) (col, col, pcol, cc, ctx);
+ if (col->nnz == 0) Q[c] = --remc;
+ scores[c] = col->nnz;
+ heap_up(heap, heap_idx, scores, heap_idx[c]);
+ heap_down(heap, heap_idx, scores, heap_size, heap_idx[c]);
+
+ }
+ rank += 1;
+ }
+ flint_free(heap);
+ flint_free(scores);
+ flint_free(heap_idx);
+
+ TEMPLATE(T, clear) (cinv, ctx);
+ TEMPLATE(T, clear) (cc, ctx);
+
+ /* Transpose L^t */
+ TEMPLATE(T, sparse_mat_transpose) (L, Lt, ctx);
+ TEMPLATE(T, sparse_mat_clear) (Lt, ctx);
+
+ /* Reorder rows and cols in L and U */
+ TEMPLATE(T, sparse_mat_permute_cols) (L, Q, ctx);
+ TEMPLATE(T, sparse_mat_permute_rows) (L, P, ctx);
+ TEMPLATE(T, sparse_mat_permute_cols) (U, Q, ctx);
+ TEMPLATE(T, sparse_mat_permute_rows) (U, P, ctx);
+ return rank;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_block_lanczos.c b/fq_sparse_mat_templates/nullspace_block_lanczos.c
new file mode 100644
index 0000000000..3e7ab6b4a1
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_block_lanczos.c
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_block_lanczos) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx)
+{
+ /* Generate random solutions to a random system Mx = b and stop when nullspace filled */
+ slong i, j, iter, nxs, *xps;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, struct) *x, **xs;
+
+ TEMPLATE(T, init) (cc, ctx);
+ x = _TEMPLATE(T, vec_init) (M->c, ctx);
+ nxs = 0;
+ xs = NULL;
+ xps = NULL;
+ for (iter = 0; iter < max_iters; )
+ {
+ if (TEMPLATE(T, sparse_mat_nullvector_block_lanczos) (x, M, block_size, state, ctx) == 0) {++iter; continue;}
+
+ /* Reduce by existing kernel vectors */
+ for (j = nxs-1; j >= 0; --j)
+ {
+ TEMPLATE(T, neg) (cc, &x[xps[j]], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (x, xs[j], M->c, cc, ctx);
+ }
+
+ /* Normalize last nonzero entry to 1 */
+ for (i = M->c-1; i >= 0 && TEMPLATE(T, is_zero) (&x[i], ctx); --i);
+ if (i == -1) {++iter; continue;} /* x in span of xs, nullspace probably complete */
+ TEMPLATE(T, inv) (cc, &x[i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (x, x, M->c, cc, ctx);
+
+ /* Reduce previous vectors by this one */
+ for (j = 0; j < nxs; ++j)
+ {
+ TEMPLATE(T, neg) (cc, &xs[j][i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (xs[j], x, M->c, cc, ctx);
+ }
+
+ /* Insert into list of vectors in nullspace (ordered by pivot) */
+ xs = realloc(xs, (nxs+1)*sizeof(*xs));
+ xps = realloc(xps, (nxs+1)*sizeof(*xps));
+ for (j = 0; j < nxs && i > xps[j]; ++j);
+ memmove(xs + j + 1, xs + j, (nxs - j)*sizeof(*xs));
+ memmove(xps + j + 1, xps + j, (nxs - j)*sizeof(*xps));
+ xps[j] = i;
+ xs[j] = x;
+ nxs += 1;
+ x = _TEMPLATE(T, vec_init) (M->c, ctx); /* New vector for next iteration */
+ iter = 0;
+ }
+ flint_free(xps);
+ TEMPLATE(T, clear) (cc, ctx);
+ _TEMPLATE(T, vec_clear) (x, M->c, ctx);
+ TEMPLATE(T, mat_init) (X, M->c, nxs, ctx);
+ for (i = 0; i < nxs; ++i)
+ {
+ for (j = 0; j < M->c; ++j)
+ TEMPLATE(T, set) (&X->rows[j][i], &xs[i][j], ctx);
+ _TEMPLATE(T, vec_clear) (xs[i], M->c, ctx);
+ }
+ flint_free(xs);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_block_wiedemann.c b/fq_sparse_mat_templates/nullspace_block_wiedemann.c
new file mode 100644
index 0000000000..ffe439616e
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_block_wiedemann.c
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_block_wiedemann) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx)
+{
+ /* Generate random solutions to a random system Mx = b and stop when nullspace filled */
+ slong i, j, iter, nxs, *xps;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, struct) *x, **xs;
+
+ TEMPLATE(T, init) (cc, ctx);
+ x = _TEMPLATE(T, vec_init) (M->c, ctx);
+ nxs = 0;
+ xs = NULL;
+ xps = NULL;
+ for (iter = 0; iter < max_iters; )
+ {
+ if (TEMPLATE(T, sparse_mat_nullvector_block_wiedemann) (x, M, block_size, state, ctx) == 0) {++iter; continue;}
+
+ /* Reduce by existing kernel vectors */
+ for (j = nxs-1; j >= 0; --j)
+ {
+ TEMPLATE(T, neg) (cc, &x[xps[j]], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (x, xs[j], M->c, cc, ctx);
+ }
+
+ /* Normalize last nonzero entry to 1 */
+ for (i = M->c-1; i >= 0 && TEMPLATE(T, is_zero) (&x[i], ctx); --i);
+ if (i == -1) {++iter; continue;} /* x in span of xs, nullspace probably complete */
+ TEMPLATE(T, inv) (cc, &x[i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (x, x, M->c, cc, ctx);
+
+ /* Reduce previous vectors by this one */
+ for (j = 0; j < nxs; ++j)
+ {
+ TEMPLATE(T, neg) (cc, &xs[j][i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (xs[j], x, M->c, cc, ctx);
+ }
+
+ /* Insert into list of vectors in nullspace (ordered by pivot) */
+ xs = realloc(xs, (nxs+1)*sizeof(*xs));
+ xps = realloc(xps, (nxs+1)*sizeof(*xps));
+ for (j = 0; j < nxs && i > xps[j]; ++j);
+ memmove(xs + j + 1, xs + j, (nxs - j)*sizeof(*xs));
+ memmove(xps + j + 1, xps + j, (nxs - j)*sizeof(*xps));
+ xps[j] = i;
+ xs[j] = x;
+ nxs += 1;
+ x = _TEMPLATE(T, vec_init) (M->c, ctx); /* New vector for next iteration */
+ iter = 0;
+ }
+ flint_free(xps);
+ TEMPLATE(T, mat_init) (X, M->c, nxs, ctx);
+ for (i = 0; i < nxs; ++i)
+ {
+ for (j = 0; j < M->c; ++j)
+ TEMPLATE(T, set) (&X->rows[j][i], &xs[i][j], ctx);
+ _TEMPLATE(T, vec_clear) (xs[i], M->c, ctx);
+ }
+ TEMPLATE(T, clear) (cc, ctx);
+ _TEMPLATE(T, vec_clear) (x, M->c, ctx);
+
+ flint_free(xs);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_lanczos.c b/fq_sparse_mat_templates/nullspace_lanczos.c
new file mode 100644
index 0000000000..7fbbadd999
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_lanczos.c
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_lanczos) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx)
+{
+ /* Generate random solutions to a random system Mx = b and stop when nullspace filled */
+ slong i, j, iter, nxs, *xps;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, struct) *x, **xs;
+
+ TEMPLATE(T, init) (cc, ctx);
+ x = _TEMPLATE(T, vec_init) (M->c, ctx);
+ nxs = 0;
+ xs = NULL;
+ xps = NULL;
+ for (iter = 0; iter < max_iters; )
+ {
+ if (TEMPLATE(T, sparse_mat_nullvector_lanczos) (x, M, state, ctx) == 0) {++iter; continue;}
+
+ /* Reduce by existing kernel vectors */
+ for (j = nxs-1; j >= 0; --j)
+ {
+ TEMPLATE(T, neg) (cc, &x[xps[j]], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (x, xs[j], M->c, cc, ctx);
+ }
+
+ /* Normalize last nonzero entry to 1 */
+ for (i = M->c-1; i >= 0 && TEMPLATE(T, is_zero) (&x[i], ctx); --i);
+ if (i == -1) {++iter; continue;} /* x in span of xs, nullspace probably complete */
+ TEMPLATE(T, inv) (cc, &x[i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (x, x, M->c, cc, ctx);
+
+ /* Reduce previous vectors by this one */
+ for (j = 0; j < nxs; ++j)
+ {
+ TEMPLATE(T, neg) (cc, &xs[j][i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (xs[j], x, M->c, cc, ctx);
+ }
+
+ /* Insert into list of vectors in nullspace (ordered by pivot) */
+ xs = realloc(xs, (nxs+1)*sizeof(*xs));
+ xps = realloc(xps, (nxs+1)*sizeof(*xps));
+ for (j = 0; j < nxs && i > xps[j]; ++j);
+ memmove(xs + j + 1, xs + j, (nxs - j)*sizeof(*xs));
+ memmove(xps + j + 1, xps + j, (nxs - j)*sizeof(*xps));
+ xps[j] = i;
+ xs[j] = x;
+ nxs += 1;
+ x = _TEMPLATE(T, vec_init) (M->c, ctx); /* New vector for next iteration */
+ iter = 0;
+ }
+ flint_free(xps);
+ TEMPLATE(T, clear) (cc, ctx);
+ _TEMPLATE(T, vec_clear) (x, M->c, ctx);
+ TEMPLATE(T, mat_init) (X, M->c, nxs, ctx);
+ for (i = 0; i < nxs; ++i)
+ {
+ for (j = 0; j < M->c; ++j)
+ TEMPLATE(T, set) (&X->rows[j][i], &xs[i][j], ctx);
+ _TEMPLATE(T, vec_clear) (xs[i], M->c, ctx);
+ }
+ flint_free(xs);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_lu.c b/fq_sparse_mat_templates/nullspace_lu.c
new file mode 100644
index 0000000000..26a460374f
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_lu.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_lu) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong rk, *P, *Q, *Qi, i, j;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, sparse_mat_t) L, U;
+ TEMPLATE(T, sparse_entry_struct) *e;
+ TEMPLATE(T, sparse_vec_struct) *Urow;
+ TEMPLATE(T, struct) *Xrow;
+
+ P = flint_malloc(M->r * sizeof(*P));
+ Q = flint_malloc(M->c * sizeof(*Q));
+ TEMPLATE(T, init) (cc, ctx);
+ TEMPLATE(T, sparse_mat_init) (L, M->r, M->c, ctx);
+ TEMPLATE(T, sparse_mat_init) (U, M->r, M->c, ctx);
+ rk = TEMPLATE(T, sparse_mat_lu) (P, Q, L, U, M, ctx);
+ flint_free(P);
+ TEMPLATE(T, sparse_mat_clear) (L, ctx);
+ for (i = 0; i < rk; ++i)
+ {
+ TEMPLATE(T, inv) (cc, U->rows[i].entries[0].val, ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_mul, T)) (&U->rows[i], &U->rows[i], cc, ctx);
+ }
+ TEMPLATE(T, mat_init) (X, M->c, M->c-rk, ctx);
+ if (rk != M->c)
+ {
+ /* Invert permutation */
+ Qi = flint_malloc(M->c * sizeof(*Qi));
+ for (i = 0; i < M->c; ++i) Qi[Q[i]] = i;
+
+ /* Mssign unit vectors to non-pivot columns */
+ for (i = M->c-1; i >= rk; --i) TEMPLATE(T, one) (&X->rows[Qi[i]][i-rk], ctx);
+ for (i = rk-1; i >= 0; --i)
+ {
+ Urow = &U->rows[i];
+ Xrow = X->rows[Qi[i]];
+ for (j = 1; j < Urow->nnz; ++j)
+ {
+ e = &Urow->entries[j];
+ /* Do in-place row elimination */
+ TEMPLATE(T, neg) (cc, e->val, ctx);
+ if (e->ind < rk) _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (Xrow, X->rows[Qi[e->ind]], X->c, cc, ctx);
+ else TEMPLATE(T, sub) (&Xrow[e->ind-rk], &Xrow[e->ind-rk], e->val, ctx);
+ }
+
+ }
+ flint_free(Qi);
+ }
+ flint_free(Q);
+ TEMPLATE(T, clear) (cc, ctx);
+ TEMPLATE(T, sparse_mat_clear) (U, ctx);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_rref.c b/fq_sparse_mat_templates/nullspace_rref.c
new file mode 100644
index 0000000000..aec35ecee9
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_rref.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_rref) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, j, rk, numc, *Q;
+ TEMPLATE(T, struct) *Xrow;
+ TEMPLATE(T, sparse_mat_t) R;
+ TEMPLATE(T, sparse_vec_struct) *Rrow;
+ TEMPLATE(T, sparse_mat_init) (R, M->r, M->c, ctx);
+ TEMPLATE(T, sparse_mat_set) (R, M, ctx);
+ rk = TEMPLATE(T, sparse_mat_rref) (R, ctx);
+ TEMPLATE(T, mat_init) (X, M->c, M->c-rk, ctx);
+ if (rk != M->c)
+ {
+ numc = 0;
+ /* Mark which cols are pivots and enumerate the nonpivots */
+ Q = flint_calloc(M->c, sizeof(*Q));
+ for (i = 0; i < rk; ++i)
+ Q[R->rows[i].entries->ind] = -1;
+ for (i = 0; i < M->c; ++i)
+ if (Q[i]==UWORD(0)) Q[i] = numc++, TEMPLATE(T, one) (&X->rows[i][Q[i]], ctx);
+
+ /* For each pivot col, set the corresponding row in X as */
+ /* the negative of the associated row in R (reordered by Q) */
+ for (i = 0; i < rk; ++i)
+ {
+ Rrow = &R->rows[i];
+ Xrow = X->rows[Rrow->entries[0].ind];
+ for (j = 1; j < Rrow->nnz; ++j)
+ TEMPLATE(T, neg) (&Xrow[Q[Rrow->entries[j].ind]], Rrow->entries[j].val, ctx);
+ }
+ flint_free(Q);
+ }
+ TEMPLATE(T, sparse_mat_clear) (R, ctx);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/nullspace_wiedemann.c b/fq_sparse_mat_templates/nullspace_wiedemann.c
new file mode 100644
index 0000000000..950b7126b7
--- /dev/null
+++ b/fq_sparse_mat_templates/nullspace_wiedemann.c
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_nullspace_wiedemann) (TEMPLATE(T, mat_t) X, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong max_iters, const TEMPLATE(T, ctx_t) ctx)
+{
+ /* Generate random solutions to a random system Mx = b and stop when nullspace filled */
+ slong i, j, iter, nxs, *xps;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, struct) *x, **xs;
+
+ TEMPLATE(T, init) (cc, ctx);
+ x = _TEMPLATE(T, vec_init) (M->c, ctx);
+ nxs = 0;
+ xs = NULL;
+ xps = NULL;
+ for (iter = 0; iter < max_iters; )
+ {
+ if (TEMPLATE(T, sparse_mat_nullvector_wiedemann) (x, M, state, ctx) == 0) {++iter; continue;}
+
+ /* Reduce by existing kernel vectors */
+ for (j = nxs-1; j >= 0; --j)
+ {
+ TEMPLATE(T, neg) (cc, &x[xps[j]], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (x, xs[j], M->c, cc, ctx);
+ }
+
+ /* Normalize last nonzero entry to 1 */
+ for (i = M->c-1; i >= 0 && TEMPLATE(T, is_zero) (&x[i], ctx); --i);
+ if (i == -1) {++iter; continue;} /* x in span of xs, nullspace probably complete */
+ TEMPLATE(T, inv) (cc, &x[i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (x, x, M->c, cc, ctx);
+
+ /* Reduce previous vectors by this one */
+ for (j = 0; j < nxs; ++j)
+ {
+ TEMPLATE(T, neg) (cc, &xs[j][i], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (xs[j], x, M->c, cc, ctx);
+ }
+
+ /* Insert into list of vectors in nullspace (ordered by pivot) */
+ xs = realloc(xs, (nxs+1)*sizeof(*xs));
+ xps = realloc(xps, (nxs+1)*sizeof(*xps));
+ for (j = 0; j < nxs && i > xps[j]; ++j);
+ memmove(xs + j + 1, xs + j, (nxs - j)*sizeof(*xs));
+ memmove(xps + j + 1, xps + j, (nxs - j)*sizeof(*xps));
+ xps[j] = i;
+ xs[j] = x;
+ nxs += 1;
+ x = _TEMPLATE(T, vec_init) (M->c, ctx); /* New vector for next iteration */
+ iter = 0;
+ }
+ flint_free(xps);
+ TEMPLATE(T, mat_init) (X, M->c, nxs, ctx);
+ for (i = 0; i < nxs; ++i)
+ {
+ for (j = 0; j < M->c; ++j)
+ TEMPLATE(T, set) (&X->rows[j][i], &xs[i][j], ctx);
+ _TEMPLATE(T, vec_clear) (xs[i], M->c, ctx);
+ }
+ TEMPLATE(T, clear) (cc, ctx);
+ _TEMPLATE(T, vec_clear) (x, M->c, ctx);
+
+ flint_free(xs);
+ return X->c;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/print_pretty.c b/fq_sparse_mat_templates/print_pretty.c
new file mode 100644
index 0000000000..9189c984cf
--- /dev/null
+++ b/fq_sparse_mat_templates/print_pretty.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+void
+TEMPLATE(T, sparse_mat_print_pretty) (const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i;
+ char row_fmt[FLINT_BITS + 5];
+ flint_sprintf(row_fmt, "%%%dwd: ", n_sizeinbase(M->r, 10));
+
+ flint_printf("<%wd x %wd sparse integer matrix mod %w>\n",
+ M->r, M->c, ctx);
+
+ for (i = 0; i < M->r; i++)
+ {
+ flint_printf(row_fmt, i);
+ TEMPLATE(T, sparse_vec_print_pretty) (&M->rows[i], M->c_off, M->c, ctx);
+ }
+}
+
+
+#endif
diff --git a/fq_sparse_mat_templates/randtest.c b/fq_sparse_mat_templates/randtest.c
new file mode 100644
index 0000000000..b53cde488e
--- /dev/null
+++ b/fq_sparse_mat_templates/randtest.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+void
+TEMPLATE(T, sparse_mat_randtest) (TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, slong min_nnz, slong max_nnz, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, nnz;
+
+ for (i = 0; i < M->r; ++i)
+ {
+ nnz = n_randint(state, max_nnz+1);
+ nnz = FLINT_MAX(nnz, min_nnz);
+ TEMPLATE(T, sparse_vec_randtest) (&M->rows[i], state, nnz, M->c, ctx);
+ }
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/rref.c b/fq_sparse_mat_templates/rref.c
new file mode 100644
index 0000000000..9b675457ca
--- /dev/null
+++ b/fq_sparse_mat_templates/rref.c
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+slong TEMPLATE(T, sparse_mat_rref) (TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong *P;
+ slong j, r, c, pr, pc, rank, remr;
+ TEMPLATE(T, t) cinv, cc;
+ TEMPLATE(T, sparse_mat_t) Mt;
+ TEMPLATE(T, sparse_vec_struct) *pcol, *prow, *row, *col;
+
+ if (M->r == 0 || M->c == 0) return 0;
+ P = flint_malloc(M->r*sizeof(*P));
+ TEMPLATE(T, init) (cinv, ctx);
+ TEMPLATE(T, init) (cc, ctx);
+ TEMPLATE(T, sparse_mat_init) (Mt, M->c, M->r, ctx);
+
+ /* Make transpose for doing column eliminations */
+ TEMPLATE(T, sparse_mat_transpose) (Mt, M, ctx);
+
+ /* Set up permutations */
+ remr = M->r;
+ for (r = 0; r < M->r; ++r)
+ {
+ if (!M->rows[r].nnz || M->rows[r].entries[0].ind >= M->c) P[r] = --remr;
+ else P[r] = -1;
+ }
+
+ /* Run elimination */
+ rank = 0;
+ for (pc = 0; pc < M->c; ++pc)
+ {
+ pcol = &Mt->rows[pc];
+
+ /* Get lowest weight incident row not used as previous pivot */
+ pr = -1, prow = NULL;
+ for (j = 0; j < pcol->nnz; ++j)
+ {
+ r = pcol->entries[j].ind, row = &M->rows[r];
+ if (P[r] >= 0) continue;
+ if (pr==-1 || (row->nnz < prow->nnz)) pr = r, prow = row;
+ }
+ if (pr == -1) continue;
+ P[pr] = rank;
+
+ TEMPLATE(T, inv) (cinv, *TEMPLATE(T, sparse_vec_at) (prow, pc, ctx), ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_mul, T)) (prow, prow, cinv, ctx);
+
+ /* Gaussian eliminate rows */
+ for (j = 0; j < pcol->nnz; ++j)
+ {
+ r = pcol->entries[j].ind, row = &M->rows[r];
+ if (r == pr) {TEMPLATE(T, zero) (pcol->entries[j].val, ctx); continue;}
+
+ TEMPLATE(T, neg) (cc, *TEMPLATE(T, sparse_vec_at) (row, pc, ctx), ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_addmul, T)) (row, row, prow, cc, ctx);
+ if (row->nnz == 0 || row->entries[0].ind >= M->c) P[r] = --remr;
+ }
+ /* Gaussian eliminate cols */
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_mul, T)) (pcol, pcol, cinv, ctx);
+ for (j = 0; j < prow->nnz; ++j)
+ {
+ c = prow->entries[j].ind, col = &Mt->rows[c];
+ if (c >= M->c || c == pc) continue;
+ TEMPLATE(T, neg) (cc, *TEMPLATE(T, sparse_vec_at) (col, pr, ctx), ctx);
+ TEMPLATE(T, TEMPLATE(sparse_vec_scalar_addmul, T)) (col, col, pcol, cc, ctx);
+ }
+ rank += 1;
+ }
+ /* Reorder rows */
+ TEMPLATE(T, sparse_mat_permute_rows) (M, P, ctx);
+
+ flint_free(P);
+ TEMPLATE(T, clear) (cinv, ctx);
+ TEMPLATE(T, clear) (cc, ctx);
+ TEMPLATE(T, sparse_mat_clear) (Mt, ctx);
+
+ return rank;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_block_lanczos.c b/fq_sparse_mat_templates/solve_block_lanczos.c
new file mode 100644
index 0000000000..6f3a4cacce
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_block_lanczos.c
@@ -0,0 +1,260 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ Algorithm taken from P. Montgomery, "A Block Lanczos Algorithm for
+ Finding Dependencies over GF(2)", Advances in Cryptology - EUROCRYPT '95
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+/* Run row Gaussian elimination on first b block of [T I], save that, */
+/* if no pivot is found for a given column c, we Gaussian eliminate */
+/* the column c + b and zero out the row c. In addition, we reorder */
+/* columns so that ones corresponding to zero entries in S go first. */
+/* See Figure 1 in the above reference for details. */
+static int compute_nWi_S(TEMPLATE(T, mat_t) nWi, int *S, const TEMPLATE(T, mat_t) Torig, const TEMPLATE(T, ctx_t) ctx)
+{
+
+ const slong b = Torig->r;
+ slong pc, i, j, rk = 0;
+ slong *P;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, mat_t) T;
+ TEMPLATE(T, mat_struct) *X;
+
+ P = flint_malloc(b * sizeof(*P));
+ TEMPLATE(T, init) (cc, ctx);
+ TEMPLATE(T, mat_init) (T, b, b, ctx);
+ TEMPLATE(T, mat_set) (T, Torig, ctx);
+ TEMPLATE(T, mat_one) (nWi, ctx);
+
+ /* Set permutation to have previously dependent vectors at front */
+ P = flint_malloc(b*sizeof(*P));
+ j = 0;
+ for (i = 0; i < b; ++i) if (!S[i]) P[j++] = i;
+ for (i = 0; i < b; ++i) if (S[i]) P[j++] = i;
+
+ for (j = 0; j < b; ++j)
+ {
+ pc = P[j]; /* Pivot col */
+
+ /* Find viable pivot row (from T if possible, then from W) */
+ for (X = T, i = j; i < b && TEMPLATE(T, is_zero)(&X->rows[P[i]][pc], ctx); ++i);
+ if (i == b)
+ for (X = nWi, i = j; i < b && TEMPLATE(T, is_zero)(&X->rows[P[i]][pc], ctx); ++i);
+ S[pc] = X == T; /* Viable column in V */
+ TEMPLATE(T, mat_swap_rows) (T, NULL, pc, P[i], ctx);
+ TEMPLATE(T, mat_swap_rows) (nWi, NULL, pc, P[i], ctx); /* Now pivot row = pivot col */
+
+ /* Make pivot one */
+ TEMPLATE(T, inv) (cc, &X->rows[pc][pc], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (T->rows[pc], T->rows[pc], b, cc, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (nWi->rows[pc], nWi->rows[pc], b, cc, ctx);
+
+ /* Kill all other entries in pivot column */
+ for (i = 0; i < b; ++i)
+ {
+ TEMPLATE(T, neg) (cc, &X->rows[P[i]][pc], ctx);
+ if (i == j || TEMPLATE(T, is_zero) (cc, ctx)) continue;
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (T->rows[P[i]], T->rows[pc], T->c, cc, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (nWi->rows[P[i]], nWi->rows[pc], nWi->c, cc, ctx);
+ }
+ if (S[pc]) rk++; /* Count viable columns */
+ else
+ {
+ /* Kill row of both matrices */
+ _TEMPLATE(T, vec_zero) (T->rows[pc], b, ctx);
+ _TEMPLATE(T, vec_zero) (nWi->rows[pc], b, ctx);
+ }
+ }
+ TEMPLATE(T, mat_neg) (nWi, nWi, ctx);
+ TEMPLATE(T, mat_clear) (T, ctx);
+
+ return rk;
+}
+
+static void kill_columns(TEMPLATE(T, mat_t) M, int *good, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong r, c;
+ for (c = 0; c < M->c; ++c)
+ if (good[c] == 0)
+ for (r = 0; r < M->r; ++r)
+ TEMPLATE(T, zero) (&M->rows[r][c], ctx);
+}
+
+int TEMPLATE(T, sparse_mat_solve_block_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ int ret = 0;
+ slong i, prev_i, next_i, iter, cur_dim, total_dim = 0;
+ TEMPLATE(T, sparse_mat_t) Mt; /* Transpose of M, we work with A = MtM */
+ TEMPLATE(T, mat_struct) V[3]; /* Keep track of current vector and two previous ones */
+ TEMPLATE(T, mat_t) MV; /* Application of M to V */
+ TEMPLATE(T, mat_t) AV; /* Application of Mt to MV */
+ int *SSt; /* S is the maximal projection s.t. (VS)^tAVS is invertible, so SSt kills the dropped columns */
+ TEMPLATE(T, mat_struct) nWi[3]; /* -S((VS)^tAVS)^-1S^t */
+ TEMPLATE(T, mat_t) VSSt; /* V with invalid vectors zeroed out */
+ TEMPLATE(T, mat_t) T; /* Used to store transposes for inner products */
+ TEMPLATE(T, mat_t) VtAV; /* Inner product _A */
+ TEMPLATE(T, mat_t) AVtAVSSt_VtAV; /* Sum _A SS^t + _A, shared by two updates */
+ TEMPLATE(T, mat_t) DEF; /* Used to store coefficient matrices D, E, and F */
+ TEMPLATE(T, mat_t) I, tmp; /* I_{b x b} */
+ TEMPLATE(T, struct) *Mtb, *SStVtMtb, *WiSStVtMtb, *VSStWiSStVtMtb; /* Intermediate elements in x update */
+ if (_TEMPLATE(T, vec_is_zero) (b, M->r, ctx))
+ {
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ return 1;
+ }
+ TEMPLATE(T, sparse_mat_init) (Mt, M->c, M->r, ctx);
+ for (i = 0; i < 3; ++i) TEMPLATE(T, mat_init) (&V[i], M->c, block_size, ctx);
+ TEMPLATE(T, mat_init) (MV, M->r, block_size, ctx); /* Intermediate product */
+ TEMPLATE(T, mat_init) (AV, M->c, block_size, ctx); /* Symmetric product */
+ SSt = flint_malloc(block_size*sizeof(*SSt));
+ for (i = 0; i < 3; ++i) TEMPLATE(T, mat_init) (&nWi[i], block_size, block_size, ctx);
+ TEMPLATE(T, mat_init) (VSSt, M->c, block_size, ctx); /* Transpose for computing matrix dot products */
+ TEMPLATE(T, mat_init) (T, block_size, M->c, ctx); /* Transpose for computing matrix dot products */
+ TEMPLATE(T, mat_init) (VtAV, block_size, block_size, ctx);
+ TEMPLATE(T, mat_init) (AVtAVSSt_VtAV, block_size, block_size, ctx); /* (AV)^T(AV) + VtAV */
+ TEMPLATE(T, mat_init) (DEF, block_size, block_size, ctx); /* Shared by D, E, and F */
+ TEMPLATE(T, mat_init) (I, block_size, block_size, ctx);
+ TEMPLATE(T, mat_init) (tmp, block_size, block_size, ctx);
+ Mtb = _TEMPLATE(T, vec_init) (M->c, ctx);
+ SStVtMtb = _TEMPLATE(T, vec_init) (block_size, ctx);
+ WiSStVtMtb = _TEMPLATE(T, vec_init) (block_size, ctx);
+ VSStWiSStVtMtb = _TEMPLATE(T, vec_init) (M->c, ctx);
+
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ TEMPLATE(T, sparse_mat_transpose) (Mt, M, ctx);
+ for (i = 0; i < block_size; ++i) SSt[i] = 1;
+ TEMPLATE(T, mat_one) (I, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (Mtb, Mt, b, ctx);
+
+ /* Initialize V[0] randomly */
+ for (i = 0; i < V[0].r*V[0].c; ++i)
+ TEMPLATE(T, randtest) (&V[0].entries[i], state, ctx);
+
+ for (iter = 0; ; ++iter)
+ {
+ i = iter % 3;
+ next_i = (iter + 1) % 3;
+ prev_i = (iter + 2) % 3;
+ if (iter >= 2)
+ {
+ /* Compute the F value for this round (minus the final term) */
+ TEMPLATE(T, mat_addmul) (DEF, I, VtAV, &nWi[prev_i], ctx);
+ TEMPLATE(T, mat_mul) (tmp, &nWi[next_i], DEF, ctx);
+ TEMPLATE(T, mat_mul) (DEF, tmp, AVtAVSSt_VtAV, ctx);
+ }
+
+ /* Compute AV and V'AV */
+ TEMPLATE(T, sparse_mat_mul_mat) (MV, M, &V[i], ctx);
+ TEMPLATE(T, sparse_mat_mul_mat) (AV, Mt, MV, ctx);
+ TEMPLATE(T, mat_transpose) (T, &V[i], ctx);
+ TEMPLATE(T, mat_mul) (VtAV, T, AV, ctx);
+ if (TEMPLATE(T, mat_is_zero) (VtAV, ctx)) {ret = 1; break;}
+
+ /* Compute W^{-1} and indices of bad vectors */
+ cur_dim = compute_nWi_S(&nWi[i], SSt, VtAV, ctx);
+ total_dim += cur_dim;
+ if (cur_dim == 0 || total_dim > M->c) break; /* Ran out of vectors */
+
+ /* Update x_i = x_{i-1} - (VSS^t) W^{-1} (VSS^t)^tb */
+ TEMPLATE(T, mat_set) (VSSt, &V[i], ctx);
+ kill_columns(VSSt, SSt, ctx);
+ TEMPLATE(T, mat_transpose) (T, VSSt, ctx);
+ TEMPLATE(T, mat_mul_vec) (SStVtMtb, T, Mtb, ctx);
+ TEMPLATE(T, mat_mul_vec) (WiSStVtMtb, &nWi[i], SStVtMtb, ctx);
+ TEMPLATE(T, mat_mul_vec) (VSStWiSStVtMtb, VSSt, WiSStVtMtb, ctx);
+ _TEMPLATE(T, vec_add) (x, x, VSStWiSStVtMtb, M->c, ctx);
+
+ /**
+ * Per Equation (19), we compute the next vector
+ * V_{i+1} = AV_iS_iS_i^t + V_i D + V_{i-1} E + V_{i-2} F
+ * where
+ * D = I - W_i^-1((AV_i)^tAV_iS_iS_i^t + V_i^tAV_i)
+ * E = -W_{i-1}^-1V_i^tAV_iS_iS_i^t
+ * F = -W_{i-2}^-1(I - V_{i-1}^tAV_{i-1}W_{i-1}^-1)
+ * ((AV_{i-1})^tAV_{i-1}S_{i-1}S_{i-1}^t + V_{i-1}^tAV_{i-1})S_iS_i^t
+ **/
+ if (iter >= 2)
+ {
+ /* V_{i+1} = V_{i-2} F */
+ kill_columns(DEF, SSt, ctx);
+ TEMPLATE(T, mat_mul) (VSSt, &V[next_i], DEF, ctx);
+ TEMPLATE(T, mat_set) (&V[next_i], VSSt, ctx);
+ }
+ if (iter >= 1)
+ {
+ /* V_{i+1} += V_{i-1} E */
+ TEMPLATE(T, mat_mul) (DEF, &nWi[prev_i], VtAV, ctx);
+ kill_columns(DEF, SSt, ctx);
+ TEMPLATE(T, mat_addmul) (&V[next_i], &V[next_i], &V[prev_i], DEF, ctx);
+ }
+ /* V_{i+1} += V_i D */
+ TEMPLATE(T, mat_transpose) (T, AV, ctx);
+ TEMPLATE(T, mat_mul) (tmp, T, AV, ctx);
+ kill_columns(tmp, SSt, ctx);
+ TEMPLATE(T, mat_add) (AVtAVSSt_VtAV, tmp, VtAV, ctx);
+ TEMPLATE(T, mat_addmul) (DEF, I, &nWi[i], AVtAVSSt_VtAV, ctx);
+ TEMPLATE(T, mat_addmul) (&V[next_i], &V[next_i], &V[i], DEF, ctx);
+
+ /* V_{i+1} += AVSS^t */
+ kill_columns(AV, SSt, ctx);
+ TEMPLATE(T, mat_add) (&V[next_i], &V[next_i], AV, ctx);
+
+ if (TEMPLATE(T, mat_is_zero) (&V[i], ctx)) {ret = 1; break;}
+ }
+ _TEMPLATE(T, vec_neg) (x, x, M->c, ctx);
+ TEMPLATE(T, sparse_mat_clear) (Mt, ctx);
+ for (i = 0; i < 3; ++i) TEMPLATE(T, mat_clear) (&V[i], ctx);
+ TEMPLATE(T, mat_clear) (MV, ctx);
+ TEMPLATE(T, mat_clear) (AV, ctx);
+ flint_free(SSt);
+ for (i = 0; i < 3; ++i) TEMPLATE(T, mat_clear) (&nWi[i], ctx);
+ TEMPLATE(T, mat_clear) (T, ctx);
+ TEMPLATE(T, mat_clear) (VtAV, ctx);
+ TEMPLATE(T, mat_clear) (VSSt, ctx);
+ TEMPLATE(T, mat_clear) (AVtAVSSt_VtAV, ctx);
+ TEMPLATE(T, mat_clear) (DEF, ctx);
+ TEMPLATE(T, mat_clear) (I, ctx);
+ TEMPLATE(T, mat_clear) (tmp, ctx);
+ _TEMPLATE(T, vec_clear) (Mtb, M->c, ctx);
+ _TEMPLATE(T, vec_clear) (SStVtMtb, block_size, ctx);
+ _TEMPLATE(T, vec_clear) (WiSStVtMtb, block_size, ctx);
+ _TEMPLATE(T, vec_clear) (VSStWiSStVtMtb, M->c, ctx);
+ return ret;
+}
+
+int TEMPLATE(T, sparse_mat_nullvector_block_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ int ret = 1;
+ TEMPLATE(T, struct) *x2, *b;
+ x2 = _TEMPLATE(T, vec_init) (M->c, ctx);
+ b = _TEMPLATE(T, vec_init) (M->r, ctx);
+
+ _TEMPLATE(T, vec_randtest) (x, state, M->c, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (b, M, x, ctx);
+ if (TEMPLATE(T, sparse_mat_solve_block_lanczos) (x2, M, b, block_size, state, ctx) == 0) ret = 0; /* Lanczos failed */
+ if (ret)
+ {
+ _TEMPLATE(T, vec_sub) (x, x, x2, M->c, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (b, M, x, ctx);
+ ret = !_TEMPLATE(T, vec_is_zero) (x, M->c, ctx) && _TEMPLATE(T, vec_is_zero) (b, M->r, ctx);
+ }
+ _TEMPLATE(T, vec_clear) (x2, M->c, ctx);
+ _TEMPLATE(T, vec_clear) (b, M->r, ctx);
+ return ret;
+}
+
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_block_wiedemann.c b/fq_sparse_mat_templates/solve_block_wiedemann.c
new file mode 100644
index 0000000000..71298aa459
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_block_wiedemann.c
@@ -0,0 +1,261 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+/* Compute S_i=(M^j Y)_{0...b-1}^T for i = 0,...,ns-1 */
+static void make_block_sequences(TEMPLATE(T, mat_struct) *S, slong ns, const TEMPLATE(T, sparse_mat_t) M, TEMPLATE(T, mat_struct) Y[2], const TEMPLATE(T, ctx_t) ctx)
+{
+ slong iter, i, b = Y->c;
+ TEMPLATE(T, mat_struct) W[2];
+ for (i = 0; i < 2; ++i) TEMPLATE(T, mat_window_init) (&W[i], &Y[i], 0, 0, b, b, ctx);
+ for (i = iter = 0; iter < ns; ++iter, i = 1-i)
+ {
+ if (iter > 0) TEMPLATE(T, sparse_mat_mul_mat) (&Y[i], M, &Y[1-i], ctx);
+ TEMPLATE(T, mat_transpose) (&S[iter], &W[i], ctx);
+ }
+ for (i = 0; i < 2; ++i) TEMPLATE(T, mat_window_clear) (&W[i], ctx);
+}
+
+/**
+ * Run Guassian elimination on the first b columns of the augmented
+ * matrix M = [ D | I], yielding a final matrix
+ * [ | ] [ Z | ]
+ * [ D | I ] -> [---| tau ]
+ * [ | ] [ L | ]
+ * where the the number of nonzero rows in Z is the ith rank. We choose
+ * the pivot row for a given column to be the one with minimal degree.
+**/
+static void coppersmith_aux_gauss(TEMPLATE(T, mat_t) M, slong *d, const TEMPLATE(T, ctx_t) ctx)
+{
+ const slong b = M->r/2;
+ slong pr, pc, r, tmp;
+ slong *gamma;
+ TEMPLATE(T, t) cinv, cc;
+
+ TEMPLATE(T, init) (cinv, ctx);
+ TEMPLATE(T, init) (cc, ctx);
+
+ /* Keep track of viable rows */
+ gamma = flint_malloc(b*sizeof(*gamma));
+ for (r = 0; r < b; ++r) gamma[r] = 1;
+
+ for (pc = 0; pc < b; ++pc)
+ {
+ /* Set the pivot row to be the minimum degree row incident on column pc */
+ pr = b + pc;
+ for (r = 0; r < b; r++)
+ if (gamma[r] && !TEMPLATE(T, is_zero) (&M->rows[r][pc], ctx) && d[r] < d[pr]) pr = r;
+ if (TEMPLATE(T, is_zero) (&M->rows[pr][pc], ctx)) continue;
+
+
+ /* Try to move pivot row to appropriate position (if not already there) */
+ if (pr != b + pc)
+ {
+ tmp = d[pr]; d[pr] = d[b+pc]; d[b+pc] = tmp;
+
+ if (!TEMPLATE(T, is_zero) (&M->rows[b + pc][pr], ctx))
+ TEMPLATE(T, mat_swap_rows) (M, NULL, pr, b + pc, ctx), pr = b + pc;
+ else /* Need to make new auxiliary vector and remove r from use */
+ _TEMPLATE(T, vec_add) (M->rows[b + pc], M->rows[b + pc], M->rows[pr], 3*b, ctx), gamma[pr] = 0;
+ }
+ TEMPLATE(T, inv) (cinv, &M->rows[pr][pc], ctx);
+
+ /* Do Gaussian elimination on first b rows */
+ for (r = 0; r < b; ++r)
+ if (gamma[r] && !TEMPLATE(T, is_zero) (&M->rows[r][pc], ctx))
+ {
+ TEMPLATE(T, mul) (cc, &M->rows[r][pc], cinv, ctx);
+ TEMPLATE(T, neg) (cc, cc, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (M->rows[r], M->rows[pr], M->c, cc, ctx);
+ }
+ }
+ TEMPLATE(T, clear) (cc, ctx);
+ TEMPLATE(T, clear) (cinv, ctx);
+ flint_free(gamma);
+}
+
+/* Stop with failure if sum(d_0 ... d_{b-1}) < delta */
+/* Stop with success if sum(d_0 ... d_{b-1}) < delta + max(d_0 ... d_{b-1}) - min(d_b ... d_{2b-1}) */
+static int coppersmith_stopping_criterion(slong *d, slong delta, slong b)
+{
+ slong tmp, r;
+
+ /* Sum degrees of generating polynomials */
+ tmp = d[0]; for (r = 1; r < b; ++r) tmp += d[r];
+ delta -= tmp;
+ if (delta < 0) return 0; /* Insufficient degree */
+
+ /* Add maximum degree of first b polys and subtract minimum degree of last b */
+ tmp = d[0]; for (r = 1; r < b; ++r) if (d[r] > tmp) tmp = d[r];
+ delta += tmp;
+ tmp = d[b]; for (r = b + 1; r < 2*b; ++r) if (d[r] < tmp) tmp = d[r];
+ delta -= tmp;
+ return delta < 0 ? 1 : -1;
+}
+
+/**
+ * Generalization of Berlekamp-Massey due to Coppersmith.
+ * Iteratively computes a sequence F representing 2b polynomials:
+ * - the first b are the current (reversed) generating polynomials
+ * - the last b are certain auxiliary polynomials.
+**/
+static int find_block_min_poly(TEMPLATE(T, mat_struct) *S, slong *d, slong n, slong delta, const TEMPLATE(T, ctx_t) ctx)
+{
+ int ret;
+ slong t;
+ slong i, k, r, b = S->r;
+ slong f_len;
+ TEMPLATE(T, mat_struct) *F;
+ TEMPLATE(T, mat_t) M, D, tau, tmp;
+
+ f_len = 1;
+ F = flint_malloc((n+1)*sizeof(*F));
+ TEMPLATE(T, mat_init) (&F[0], 2*b, b, ctx);
+ TEMPLATE(T, mat_init) (tmp, b, b, ctx);
+ for (i = 0; i < b; ++i) d[i] = 0, d[b + i] = 1, TEMPLATE(T, one) (&F[0].rows[i][i], ctx);
+
+ /* [ D | I ] -> [ ? | tau ]*/
+ TEMPLATE(T, mat_init) (M, 2*b, 3*b, ctx);
+
+ for (t = 0, ret = -1; t < n && ret == -1; ++t)
+ {
+ /* Compute discrepancy matrix and tau */
+ TEMPLATE(T, mat_window_init) (D, M, 0, 0, 2*b, b, ctx);
+ TEMPLATE(T, mat_window_init) (tau, M, 0, b, 2*b, 3*b, ctx);
+ TEMPLATE(T, mat_zero) (D, ctx);
+ for (k = 0; k <= t; ++k) TEMPLATE(T, mat_addmul) (D, D, &F[k], &S[t-k], ctx);
+ TEMPLATE(T, mat_one) (tau, ctx);
+ TEMPLATE(T, mat_window_clear) (D, ctx);
+ TEMPLATE(T, mat_window_clear) (tau, ctx);
+ coppersmith_aux_gauss(M, d, ctx);
+
+ /* Multiply F by tau * diag(I xI) */
+ TEMPLATE(T, mat_window_init) (tau, M, 0, b, 2*b, 3*b, ctx); /* Needed since gauss reorders rows */
+ TEMPLATE(T, mat_init) (&F[f_len++], 2*b, b, ctx);
+ for (k = f_len-1; k > 0; --k)
+ TEMPLATE(T, mat_mul) (&F[k], tau, &F[k-1], ctx); /* Every row multiplied by x */
+ for (k = 0; k < f_len; ++k)
+ for (r = 0; r < b; ++r) /* Divide first b rows by x */
+ {
+ if (k < f_len - 1) _TEMPLATE(T, vec_set) (F[k].rows[r], F[k+1].rows[r], b, ctx);
+ else _TEMPLATE(T, vec_zero) (F[k].rows[r], b, ctx);
+ }
+ for (r = b; r < 2*b; ++r) _TEMPLATE(T, vec_zero) (F[0].rows[r], b, ctx), d[r] += 1;
+ TEMPLATE(T, mat_window_clear) (tau, ctx);
+ ret = coppersmith_stopping_criterion(d, delta, b);
+ }
+
+ /* Copy C to S, with each row reversed according to its degree */
+ for (r = 0; r < b; ++r)
+ for (k = 0; k <= d[r]; k++)
+ _TEMPLATE(T, vec_set) (S[k].rows[r], F[d[r]-k].rows[r], b, ctx);
+
+ for (k = 0; k < f_len; ++k) TEMPLATE(T, mat_clear) (&F[k], ctx);
+ TEMPLATE(T, mat_clear) (M, ctx);
+ flint_free(F);
+ return ret;
+}
+
+static void make_block_sum(TEMPLATE(T, struct) *x, const TEMPLATE(T, mat_struct) *S, const slong *d, const TEMPLATE(T, sparse_mat_t) M, TEMPLATE(T, mat_struct) Z[2], slong l, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong i, iter, b = S->r;
+ slong dd;
+ TEMPLATE(T, struct) *xi;
+
+ /* Compute differences between nominal and real degree */
+ dd = 0;
+ while (_TEMPLATE(T, vec_is_zero) (S[dd].rows[l], b, ctx)) ++dd;
+
+ /* Simulaneously apply all polynomials in row l to iteration of M on Z */
+ xi = _TEMPLATE(T, vec_init) (M->c, ctx);
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ for (i = iter = 0; iter <= d[l]; ++iter, i = 1 - i)
+ {
+ if (iter > 0) TEMPLATE(T, sparse_mat_mul_mat) (&Z[i], M, &Z[1-i], ctx);
+ TEMPLATE(T, mat_mul_vec) (xi, &Z[i], S[dd + iter].rows[l], ctx);
+ _TEMPLATE(T, vec_add) (x, x, xi, M->c, ctx);
+ }
+ _TEMPLATE(T, vec_clear) (xi, M->c, ctx);
+}
+
+int TEMPLATE(T, sparse_mat_solve_block_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ int good = 0, ret;
+ TEMPLATE (T, struct) *x1;
+ TEMPLATE(T, sparse_vec_t) z;
+ TEMPLATE(T, sparse_mat_t) Mb;
+ if (M->r != M->c) return 0; /* TODO */
+ if (_TEMPLATE(T, vec_is_zero) (b, M->c, ctx))
+ {
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ return 1;
+ }
+
+ /* TODO: Precondition M */
+ x1 = _TEMPLATE(T, vec_init) (M->c + 1, ctx);
+ TEMPLATE(T, sparse_vec_init) (z, ctx);
+ TEMPLATE(T, sparse_mat_init) (Mb, M->r, M->c, ctx);
+ TEMPLATE(T, sparse_mat_set) (Mb, M, ctx);
+ TEMPLATE(T, sparse_mat_append_col) (Mb, b, ctx);
+ TEMPLATE(T, sparse_mat_append_row) (Mb, z, ctx);
+
+ ret = TEMPLATE(T, sparse_mat_nullvector_block_wiedemann) (x1, Mb, block_size, state, ctx);
+ if (ret && !TEMPLATE(T, is_zero) (&x1[M->c], ctx))
+ {
+ TEMPLATE(T, inv) (&x1[M->c], &x1[M->c], ctx);
+ TEMPLATE(T, neg) (&x1[M->c], &x1[M->c], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T)) (x, x1, M->c, &x1[M->c], ctx);
+ good = 1;
+ }
+ TEMPLATE(T, sparse_vec_clear) (z, ctx);
+ TEMPLATE(T, sparse_mat_clear) (Mb, ctx);
+ _TEMPLATE(T, vec_clear) (x1, M->c + 1, ctx);
+ return good;
+}
+
+int TEMPLATE(T, sparse_mat_nullvector_block_wiedemann) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, slong block_size, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ int ret = 0;
+ slong l, ns, k;
+ slong *d;
+ TEMPLATE(T, struct) *b;
+ TEMPLATE(T, mat_struct) Y[3], *S;
+ if (M->r != M->c) return 0; /* TODO */
+
+ ns = 2*M->r/block_size + 3; /* Maybe 5? */
+ S = flint_malloc(ns*sizeof(*S));
+ d = flint_calloc(2*block_size, sizeof(*d));
+ b = _TEMPLATE(T, vec_init) (M->r, ctx);
+ for (k = 0; k < ns; ++k) TEMPLATE(T, mat_init) (&S[k], block_size, block_size, ctx);
+ for (l = 0; l < 3; ++l) TEMPLATE(T, mat_init) (&Y[l], M->c, block_size, ctx);
+ do TEMPLATE(T, mat_randtest) (&Y[0], state, ctx);
+ while (TEMPLATE(T, mat_is_zero) (&Y[0], ctx));
+
+ TEMPLATE(T, sparse_mat_mul_mat) (&Y[1], M, &Y[0], ctx);
+ make_block_sequences(S, ns, M, &Y[1], ctx);
+ find_block_min_poly(S, d, ns, M->r, ctx);
+
+ for (l = 0; l < block_size; ++l)
+ {
+ TEMPLATE(T, mat_set) (&Y[1], &Y[0], ctx);
+ make_block_sum(x, S, d, M, Y + 1, l, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (b, M, x, ctx);
+ if (!_TEMPLATE(T, vec_is_zero) (x, M->c, ctx) && _TEMPLATE(T, vec_is_zero) (b, M->r, ctx)) {ret = 1; break;};
+ }
+ return ret;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_lanczos.c b/fq_sparse_mat_templates/solve_lanczos.c
new file mode 100644
index 0000000000..4b1c076a0d
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_lanczos.c
@@ -0,0 +1,127 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+int TEMPLATE(T, sparse_mat_solve_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ slong j, ret;
+
+ /* We assume that M is not symmetric, and work with A = M^t M */
+ TEMPLATE(T, t) cinv, cc, AvtAv, vMtb, alpha, beta;
+ TEMPLATE(T, struct) *v[2], *Mv, *Av, *Mtb, vtAv[2];
+ TEMPLATE(T, sparse_mat_t) Mt;
+
+ if (_TEMPLATE(T, vec_is_zero) (b, M->r, ctx))
+ {
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ return 1;
+ }
+
+ TEMPLATE(T, init) (cinv, ctx);
+ TEMPLATE(T, init) (cc, ctx);
+ TEMPLATE(T, init) (AvtAv, ctx);
+ TEMPLATE(T, init) (vMtb, ctx);
+ TEMPLATE(T, init) (alpha, ctx);
+ TEMPLATE(T, init) (beta, ctx);
+ TEMPLATE(T, init) (&vtAv[0], ctx);
+ TEMPLATE(T, init) (&vtAv[1], ctx);
+ v[0] = _TEMPLATE(T, vec_init) (M->c, ctx);
+ v[1] = _TEMPLATE(T, vec_init) (M->c, ctx);
+ Mv = _TEMPLATE(T, vec_init) (M->r, ctx);
+ Av = _TEMPLATE(T, vec_init) (M->c, ctx);
+ Mtb = _TEMPLATE(T, vec_init) (M->c, ctx);
+ TEMPLATE(T, sparse_mat_init) (Mt, M->c, M->r, ctx);
+
+ /* Construct transpose */
+ TEMPLATE(T, sparse_mat_transpose) (Mt, M, ctx);
+
+
+ /* Set starting data */
+ _TEMPLATE(T, vec_randtest) (v[0], state, M->c, ctx);
+ _TEMPLATE(T, vec_zero) (v[1], M->c, ctx);
+ TEMPLATE(T, one) (&vtAv[1], ctx);
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (Mtb, Mt, b, ctx);
+ for (j = 0; ; j = 1-j)
+ {
+ /* Compute M^T M v_j and check if it is orthogonal to v_j */
+ TEMPLATE(T, sparse_mat_mul_vec) (Mv, M, v[j], ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (Av, Mt, Mv, ctx);
+ _TEMPLATE(T, vec_dot) (&vtAv[j], v[j], Av, M->c, ctx);
+ if (TEMPLATE(T, is_zero) (&vtAv[j], ctx)) break; /* Can't make any more progress */
+
+ /* Update putative solution by /delta_j * v_j */
+ _TEMPLATE(T, vec_dot) (cc, v[j], Mtb, M->c, ctx);
+ TEMPLATE(T, div) (cc, cc, &vtAv[j], ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (x, v[j], M->c, cc, ctx);
+
+ /* v_{j+1} = AtAv - alpha*v_j - beta*v_{j-1}, where */
+ /* alpha = /delta_j, and */
+ /* beta = delta_j/delta_{j-1} */
+ TEMPLATE(T, div) (cc, &vtAv[j], &vtAv[1-j], ctx);
+ TEMPLATE(T, neg) (cc, cc, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_mul, T))(v[1-j], v[1-j], M->c, cc, ctx);
+ _TEMPLATE(T, vec_dot) (cc, Av, Av, M->c, ctx);
+ TEMPLATE(T, div) (cc, cc, &vtAv[j], ctx);
+ TEMPLATE(T, neg) (cc, cc, ctx);
+ _TEMPLATE(T, TEMPLATE(vec_scalar_addmul, T)) (v[1-j], v[j], M->c, cc, ctx);
+ _TEMPLATE(T, vec_add) (v[1-j], v[1-j], Av, M->c, ctx);
+ }
+ /* Check result */
+ TEMPLATE(T, sparse_mat_mul_vec) (Mv, M, x, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (Av, Mt, Mv, ctx);
+ ret = _TEMPLATE(T, vec_equal) (Av, Mtb, M->c, ctx);
+
+ /* Clear auxiliary vectors and transpose */
+ TEMPLATE(T, clear) (cc, ctx);
+ TEMPLATE(T, clear) (cinv, ctx);
+ TEMPLATE(T, clear) (AvtAv, ctx);
+ TEMPLATE(T, clear) (vMtb, ctx);
+ TEMPLATE(T, clear) (alpha, ctx);
+ TEMPLATE(T, clear) (beta, ctx);
+ TEMPLATE(T, clear) (&vtAv[0], ctx);
+ TEMPLATE(T, clear) (&vtAv[1], ctx);
+ _TEMPLATE(T, vec_clear) (v[0], M->c, ctx);
+ _TEMPLATE(T, vec_clear) (v[1], M->c, ctx);
+ _TEMPLATE(T, vec_clear) (Mv, M->r, ctx);
+ _TEMPLATE(T, vec_clear) (Av, M->c, ctx);
+ _TEMPLATE(T, vec_clear) (Mtb, M->c, ctx);
+ TEMPLATE(T, sparse_mat_clear) (Mt, ctx);
+ return ret;
+}
+
+int TEMPLATE(T, sparse_mat_nullvector_lanczos) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, flint_rand_t state, const TEMPLATE(T, ctx_t) ctx)
+{
+ int ret = 1;
+ TEMPLATE(T, struct) *x2, *b;
+ x2 = _TEMPLATE(T, vec_init) (M->c, ctx);
+ b = _TEMPLATE(T, vec_init) (M->r, ctx);
+
+ _TEMPLATE(T, vec_randtest) (x, state, M->c, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (b, M, x, ctx);
+ if (TEMPLATE(T, sparse_mat_solve_lanczos) (x2, M, b, state, ctx) == 0) ret = 0; /* Lanczos failed */
+ if (ret)
+ {
+ _TEMPLATE(T, vec_sub) (x, x, x2, M->c, ctx);
+ TEMPLATE(T, sparse_mat_mul_vec) (b, M, x, ctx);
+ ret = !_TEMPLATE(T, vec_is_zero) (x, M->c, ctx) && _TEMPLATE(T, vec_is_zero) (b, M->r, ctx);
+ }
+ _TEMPLATE(T, vec_clear) (x2, M->c, ctx);
+ _TEMPLATE(T, vec_clear) (b, M->r, ctx);
+ return ret;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_lu.c b/fq_sparse_mat_templates/solve_lu.c
new file mode 100644
index 0000000000..5946414baf
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_lu.c
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+/* PAQ = LU, Ax = b => set b' = Pb, solve Ly = b', solve Ux' = y, set x = Qx' */
+int TEMPLATE(T, sparse_mat_solve_lu) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, const TEMPLATE(T, ctx_t) ctx)
+{
+ int good = 1;
+ slong rk, *P, *Q, i;
+ TEMPLATE(T, t) cc;
+ TEMPLATE(T, struct) *bp, *y, *xp;
+ TEMPLATE(T, sparse_mat_t) L, U;
+ if (_TEMPLATE(T, vec_is_zero) (b, M->r, ctx))
+ {
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ return 1;
+ }
+
+ P = flint_malloc(M->r * sizeof(*P));
+ Q = flint_malloc(M->c * sizeof(*Q));
+ TEMPLATE(T, init) (cc, ctx);
+ bp = _TEMPLATE(T, vec_init) (M->r, ctx);
+ xp = _TEMPLATE(T, vec_init) (M->c, ctx);
+ TEMPLATE(T, sparse_mat_init) (L, M->r, M->c, ctx);
+ TEMPLATE(T, sparse_mat_init) (U, M->r, M->c, ctx);
+
+ rk = TEMPLATE(T, sparse_mat_lu) (P, Q, L, U, M, ctx);
+ y = _TEMPLATE(T, vec_init) (rk, ctx);
+
+ /* Solve Ly = b' = Pb */
+ for (i = 0; i < M->r; ++i) TEMPLATE(T, set) (&bp[P[i]], &b[i], ctx);
+
+ for (i = 0; i < rk; ++i)
+ {
+ TEMPLATE(T, sparse_vec_dot_dense) (cc, &L->rows[i], y, ctx);
+ TEMPLATE(T, sub) (&y[i], &bp[i], cc, ctx);
+ }
+ for (i = rk; i < M->r; ++i)
+ {
+ TEMPLATE(T, sparse_vec_dot_dense) (cc, &L->rows[i], y, ctx);
+ if (!TEMPLATE(T, equal) (&bp[i], cc, ctx)) {good = 0; break;}
+ }
+
+ if (good)
+ {
+ /* Find a solution for Ux' = y */
+ for (i = rk-1; i >= 0; --i)
+ {
+ TEMPLATE(T, sparse_vec_dot_dense) (&xp[i], &U->rows[i], xp, ctx);
+ TEMPLATE(T, sub) (&xp[i], &y[i], &xp[i], ctx);
+ TEMPLATE(T, div) (&xp[i], &xp[i], U->rows[i].entries[0].val, ctx);
+ }
+ for (i = 0; i < M->c; ++i) TEMPLATE(T, set) (&x[i], &xp[Q[i]], ctx);
+ }
+ TEMPLATE(T, sparse_mat_clear) (L, ctx);
+ TEMPLATE(T, sparse_mat_clear) (U, ctx);
+ _TEMPLATE(T, vec_clear) (xp, M->c, ctx);
+ _TEMPLATE(T, vec_clear) (y, rk, ctx);
+ _TEMPLATE(T, vec_clear) (bp, M->r, ctx);
+ return good;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_rref.c b/fq_sparse_mat_templates/solve_rref.c
new file mode 100644
index 0000000000..022bb21f29
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_rref.c
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#ifdef T
+
+#include
+#include "templates.h"
+
+int TEMPLATE(T, sparse_mat_solve_rref) (TEMPLATE(T, struct) *x, const TEMPLATE(T, sparse_mat_t) M, const TEMPLATE(T, struct) *b, const TEMPLATE(T, ctx_t) ctx)
+{
+ int good = 1;
+ slong i;
+ TEMPLATE(T, sparse_mat_t) Mb;
+ TEMPLATE(T, sparse_vec_struct) *row;
+ TEMPLATE(T, sparse_entry_struct) *le, *re;
+ if (_TEMPLATE(T, vec_is_zero) (b, M->r, ctx))
+ {
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ return 1;
+ }
+
+ TEMPLATE(T, sparse_mat_init) (Mb, M->r, M->c, ctx);
+
+ TEMPLATE(T, sparse_mat_set) (Mb, M, ctx);
+ TEMPLATE(T, sparse_mat_append_col) (Mb, b, ctx);
+ Mb->c = M->c;
+ TEMPLATE(T, sparse_mat_rref) (Mb, ctx);
+ Mb->c = M->c+1;
+
+ _TEMPLATE(T, vec_zero) (x, M->c, ctx);
+ for (i = 0; i < M->r; ++i)
+ {
+ row = &Mb->rows[i];
+ if (row->nnz == 0) continue;
+ le = &row->entries[0];
+ re = &row->entries[row->nnz-1];
+ /* If any row has leading col M->c, system is not solvable */
+ if (le->ind==M->c) {good = 0; break;}
+ /* Otherwise, x[lc] = lagging value */
+ if (re->ind==M->c) TEMPLATE(T, set) (&x[le->ind], re->val, ctx);
+ }
+ TEMPLATE(T, sparse_mat_clear) (Mb, ctx);
+ return good;
+}
+
+#endif
diff --git a/fq_sparse_mat_templates/solve_wiedemann.c b/fq_sparse_mat_templates/solve_wiedemann.c
new file mode 100644
index 0000000000..b67656b491
--- /dev/null
+++ b/fq_sparse_mat_templates/solve_wiedemann.c
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2010 Fredrik Johansson
+ Copyright (C) 2020 Kartik Venkatram
+
+ 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 th e Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See