1 /*
2     Copyright (C) 2013 Tom Bachmann
3 
4     This file is part of FLINT.
5 
6     FLINT is free software: you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 2.1 of the License, or
9     (at your option) any later version.  See <http://www.gnu.org/licenses/>.
10 */
11 
12 #ifndef NMOD_MATXX_H
13 #define NMOD_MATXX_H
14 
15 #include <string>
16 #include <vector>
17 
18 #include "nmod_mat.h"
19 
20 #include "nmod_vecxx.h"
21 #include "fmpz_matxx.h" // for modular reduction
22 #include "permxx.h"
23 
24 #include "flintxx/flint_exception.h"
25 #include "flintxx/ltuple.h"
26 #include "flintxx/matrix.h"
27 
28 // TODO addmul
29 // TODO default argument for mat_solve_triu etc?
30 // TODO nullspace member
31 // TODO unnecessary perm copies in set_lu*
32 
33 namespace flint {
34 FLINT_DEFINE_BINOP(solve_vec)
FLINT_DEFINE_BINOP(mul_strassen)35 FLINT_DEFINE_BINOP(mul_strassen)
36 FLINT_DEFINE_THREEARY(solve_tril)
37 FLINT_DEFINE_THREEARY(solve_tril_classical)
38 FLINT_DEFINE_THREEARY(solve_tril_recursive)
39 FLINT_DEFINE_THREEARY(solve_triu)
40 FLINT_DEFINE_THREEARY(solve_triu_classical)
41 FLINT_DEFINE_THREEARY(solve_triu_recursive)
42 
43 FLINT_DEFINE_THREEARY(multi_CRT_precomp)
44 
45 namespace detail {
46 template<class Mat>
47 struct nmod_matxx_traits : matrices::generic_traits<Mat> { };
48 } // detail
49 
50 template<class Operation, class Data>
51 class nmod_matxx_expression
52     : public expression<derived_wrapper<nmod_matxx_expression>, Operation, Data>
53 {
54 public:
55     typedef expression<derived_wrapper< ::flint::nmod_matxx_expression>,
56               Operation, Data> base_t;
57     typedef detail::nmod_matxx_traits<nmod_matxx_expression> traits_t;
58 
59     FLINTXX_DEFINE_BASICS(nmod_matxx_expression)
FLINTXX_DEFINE_CTORS(nmod_matxx_expression)60     FLINTXX_DEFINE_CTORS(nmod_matxx_expression)
61     FLINTXX_DEFINE_C_REF(nmod_matxx_expression, nmod_mat_struct, _mat)
62 
63     // These only make sense with immediates
64     nmodxx_ctx_srcref _ctx() const
65         {return nmodxx_ctx_srcref::make(_mat()->mod);}
66 
67     // These work on any expression without evaluation
estimate_ctx()68     nmodxx_ctx_srcref estimate_ctx() const
69     {
70         return tools::find_nmodxx_ctx(*this);
71     }
modulus()72     mp_limb_t modulus() const {return estimate_ctx().n();}
73 
74     template<class Expr>
create_temporary_rowscols(const Expr & e,slong rows,slong cols)75     static evaluated_t create_temporary_rowscols(
76             const Expr& e, slong rows, slong cols)
77     {
78         return evaluated_t(rows, cols, tools::find_nmodxx_ctx(e).n());
79     }
FLINTXX_DEFINE_MATRIX_METHODS(traits_t)80     FLINTXX_DEFINE_MATRIX_METHODS(traits_t)
81 
82     template<class Fmpz_mat>
83     static nmod_matxx_expression reduce(const Fmpz_mat& mat,
84             mp_limb_t modulus,
85             typename mp::enable_if<traits::is_fmpz_matxx<Fmpz_mat> >::type* = 0)
86     {
87         nmod_matxx_expression res(mat.rows(), mat.cols(), modulus);
88         fmpz_mat_get_nmod_mat(res._mat(), mat.evaluate()._mat());
89         return res;
90     }
91 
randtest(slong rows,slong cols,mp_limb_t n,frandxx & state)92     static nmod_matxx_expression randtest(slong rows, slong cols, mp_limb_t n,
93             frandxx& state)
94     {
95         nmod_matxx_expression res(rows, cols, n);
96         res.set_randtest(state);
97         return res;
98     }
randfull(slong rows,slong cols,mp_limb_t n,frandxx & state)99     static nmod_matxx_expression randfull(slong rows, slong cols, mp_limb_t n,
100             frandxx& state)
101     {
102         nmod_matxx_expression res(rows, cols, n);
103         res.set_randfull(state);
104         return res;
105     }
randrank(slong rows,slong cols,mp_limb_t n,frandxx & state,slong rank)106     static nmod_matxx_expression randrank(slong rows, slong cols, mp_limb_t n,
107             frandxx& state, slong rank)
108     {
109         nmod_matxx_expression res(rows, cols, n);
110         res.set_randrank(state, rank);
111         return res;
112     }
randtril(slong rows,slong cols,mp_limb_t n,frandxx & state,bool unit)113     static nmod_matxx_expression randtril(slong rows, slong cols, mp_limb_t n,
114             frandxx& state, bool unit)
115     {
116         nmod_matxx_expression res(rows, cols, n);
117         res.set_randtril(state, unit);
118         return res;
119     }
randtriu(slong rows,slong cols,mp_limb_t n,frandxx & state,bool unit)120     static nmod_matxx_expression randtriu(slong rows, slong cols, mp_limb_t n,
121             frandxx& state,
122             bool unit)
123     {
124         nmod_matxx_expression res(rows, cols, n);
125         res.set_randtriu(state, unit);
126         return res;
127     }
128 
129     template<class Vec>
randpermdiag(slong rows,slong cols,mp_limb_t n,frandxx & state,const Vec & v)130     static nmod_matxx_expression randpermdiag(slong rows, slong cols, mp_limb_t n,
131             frandxx& state, const Vec& v)
132     {
133         nmod_matxx_expression res(rows, cols, n);
134         res.set_randpermdiag(state, v);
135         return res;
136     }
137 
zero(slong rows,slong cols,mp_limb_t n)138     static nmod_matxx_expression zero(slong rows, slong cols, mp_limb_t n)
139         {return nmod_matxx_expression(rows, cols, n);}
140 
141     // these only make sense with targets
set_randtest(frandxx & state)142     void set_randtest(frandxx& state)
143         {nmod_mat_randtest(_mat(), state._data());}
set_randfull(frandxx & state)144     void set_randfull(frandxx& state)
145         {nmod_mat_randfull(_mat(), state._data());}
set_randrank(frandxx & state,slong rank)146     void set_randrank(frandxx& state, slong rank)
147         {nmod_mat_randrank(_mat(), state._data(), rank);}
set_randtril(frandxx & state,bool unit)148     void set_randtril(frandxx& state, bool unit)
149         {nmod_mat_randtril(_mat(), state._data(), unit);}
set_randtriu(frandxx & state,bool unit)150     void set_randtriu(frandxx& state, bool unit)
151         {nmod_mat_randtriu(_mat(), state._data(), unit);}
152 
153     template<class Vec>
set_randpermdiag(frandxx & state,const Vec & v)154     int set_randpermdiag(frandxx& state, const Vec& v)
155     {
156         return nmod_mat_randpermdiag(_mat(), state._data(), v._array(), v.size());
157     }
158 
apply_randops(frandxx & state,slong count)159     void apply_randops(frandxx& state, slong count)
160         {nmod_mat_randops(_mat(), count, state._data());}
161 
set_rref()162     slong set_rref() {return nmod_mat_rref(_mat());}
set_zero()163     void set_zero() {nmod_mat_zero(_mat());}
164 
165     typedef mp::make_tuple<slong, permxx>::type lu_rt;
166     lu_rt set_lu(bool rank_check = false)
167     {
168         lu_rt res = mp::make_tuple<slong, permxx>::make(0, permxx(rows()));
169         res.first() = nmod_mat_lu(res.second()._data(), _mat(), rank_check);
170         return res;
171     }
172     lu_rt set_lu_classical(bool rank_check = false)
173     {
174         lu_rt res = mp::make_tuple<slong, permxx>::make(0, permxx(rows()));
175         res.first() = nmod_mat_lu_classical(
176                 res.second()._data(), _mat(), rank_check);
177         return res;
178     }
179     lu_rt set_lu_recursive(bool rank_check = false)
180     {
181         lu_rt res = mp::make_tuple<slong, permxx>::make(0, permxx(rows()));
182         res.first() = nmod_mat_lu_recursive(
183                 res.second()._data(), _mat(), rank_check);
184         return res;
185     }
186 
187 
188     // these cause evaluation
rank()189     slong rank() const {return nmod_mat_rank(this->evaluate()._mat());}
is_zero()190     bool is_zero() const {return nmod_mat_is_zero(this->evaluate()._mat());}
is_empty()191     bool is_empty() const {return nmod_mat_is_empty(this->evaluate()._mat());}
is_square()192     bool is_square() const {return nmod_mat_is_square(this->evaluate()._mat());}
193 
194     // lazy members
195     FLINTXX_DEFINE_MEMBER_BINOP(solve)
196     FLINTXX_DEFINE_MEMBER_BINOP(mul_classical)
197     FLINTXX_DEFINE_MEMBER_BINOP(mul_strassen)
198     FLINTXX_DEFINE_MEMBER_UNOP(inv)
199     FLINTXX_DEFINE_MEMBER_UNOP(transpose)
200     FLINTXX_DEFINE_MEMBER_UNOP_RTYPE(nmodxx, trace)
201     FLINTXX_DEFINE_MEMBER_UNOP_RTYPE(nmodxx, det)
202     //FLINTXX_DEFINE_MEMBER_UNOP_RTYPE(???, nullspace) // TODO
203     FLINTXX_DEFINE_MEMBER_3OP(solve_tril)
204     FLINTXX_DEFINE_MEMBER_3OP(solve_tril_recursive)
205     FLINTXX_DEFINE_MEMBER_3OP(solve_tril_classical)
206     FLINTXX_DEFINE_MEMBER_3OP(solve_triu)
207     FLINTXX_DEFINE_MEMBER_3OP(solve_triu_recursive)
208     FLINTXX_DEFINE_MEMBER_3OP(solve_triu_classical)
209 };
210 
211 namespace detail {
212 struct nmod_mat_data;
213 } // detail
214 
215 typedef nmod_matxx_expression<operations::immediate, detail::nmod_mat_data> nmod_matxx;
216 typedef nmod_matxx_expression<operations::immediate,
217             flint_classes::ref_data<nmod_matxx, nmod_mat_struct> > nmod_matxx_ref;
218 typedef nmod_matxx_expression<operations::immediate, flint_classes::srcref_data<
219     nmod_matxx, nmod_matxx_ref, nmod_mat_struct> > nmod_matxx_srcref;
220 
221 template<>
222 struct matrix_traits<nmod_matxx>
223 {
224     template<class M> static slong rows(const M& m)
225     {
226         return nmod_mat_nrows(m._mat());
227     }
228     template<class M> static slong cols(const M& m)
229     {
230         return nmod_mat_ncols(m._mat());
231     }
232 
233     template<class M> static nmodxx_srcref at(const M& m, slong i, slong j)
234     {
235         return nmodxx_srcref::make(nmod_mat_entry(m._mat(), i, j),
236                 m.estimate_ctx());
237     }
238     template<class M> static nmodxx_ref at(M& m, slong i, slong j)
239     {
240         return nmodxx_ref::make(nmod_mat_entry(m._mat(), i, j),
241                 m.estimate_ctx());
242     }
243 };
244 
245 namespace traits {
246 template<> struct has_nmodxx_ctx<nmod_matxx> : mp::true_ { };
247 template<> struct has_nmodxx_ctx<nmod_matxx_ref> : mp::true_ { };
248 template<> struct has_nmodxx_ctx<nmod_matxx_srcref> : mp::true_ { };
249 } // traits
250 
251 namespace detail {
252 template<>
253 struct nmod_matxx_traits<nmod_matxx_srcref>
254     : matrices::generic_traits_srcref<nmodxx_srcref> { };
255 template<>
256 struct nmod_matxx_traits<nmod_matxx_ref>
257     : matrices::generic_traits_ref<nmodxx_ref> { };
258 template<> struct nmod_matxx_traits<nmod_matxx>
259     : matrices::generic_traits_nonref<nmodxx_ref, nmodxx_srcref> { };
260 
261 struct nmod_mat_data
262 {
263     typedef nmod_mat_t& data_ref_t;
264     typedef const nmod_mat_t& data_srcref_t;
265 
266     nmod_mat_t inner;
267 
268     nmod_mat_data(slong m, slong n, mp_limb_t modulus)
269     {
270         nmod_mat_init(inner, m, n, modulus);
271     }
272 
273     nmod_mat_data(const nmod_mat_data& o)
274     {
275         nmod_mat_init_set(inner, o.inner);
276     }
277 
278     nmod_mat_data(nmod_matxx_srcref o)
279     {
280         nmod_mat_init_set(inner, o._data().inner);
281     }
282 
283     ~nmod_mat_data() {nmod_mat_clear(inner);}
284 };
285 } // detail
286 
287 namespace matrices {
288 template<>
289 struct outsize<operations::mul_strassen_op>
290     : outsize<operations::times> { };
291 
292 template<> struct outsize<operations::solve_tril_op>
293     : outsize<operations::solve_op> { };
294 template<> struct outsize<operations::solve_tril_classical_op>
295     : outsize<operations::solve_op> { };
296 template<> struct outsize<operations::solve_tril_recursive_op>
297     : outsize<operations::solve_op> { };
298 template<> struct outsize<operations::solve_triu_op>
299     : outsize<operations::solve_op> { };
300 template<> struct outsize<operations::solve_triu_classical_op>
301     : outsize<operations::solve_op> { };
302 template<> struct outsize<operations::solve_triu_recursive_op>
303     : outsize<operations::solve_op> { };
304 }
305 
306 // temporary instantiation stuff
307 FLINTXX_DEFINE_TEMPORARY_RULES(nmod_matxx)
308 
309 #define NMOD_MATXX_COND_S FLINTXX_COND_S(nmod_matxx)
310 #define NMOD_MATXX_COND_T FLINTXX_COND_T(nmod_matxx)
311 
312 namespace traits {
313 template<class T> struct is_nmod_matxx
314     : flint_classes::is_Base<nmod_matxx, T> { };
315 } // traits
316 
317 namespace rules {
318 FLINT_DEFINE_DOIT_COND2(assignment, NMOD_MATXX_COND_T, NMOD_MATXX_COND_S,
319         nmod_mat_set(to._mat(), from._mat()))
320 
321 FLINTXX_DEFINE_SWAP(nmod_matxx, nmod_mat_swap(e1._mat(), e2._mat()))
322 
323 FLINTXX_DEFINE_EQUALS(nmod_matxx, nmod_mat_equal(e1._mat(), e2._mat()))
324 
325 FLINT_DEFINE_PRINT_PRETTY_COND(NMOD_MATXX_COND_S,
326         (nmod_mat_print_pretty(from._mat()), 1))
327 
328 FLINT_DEFINE_THREEARY_EXPR_COND3(mat_at_op, nmodxx,
329         NMOD_MATXX_COND_S, traits::fits_into_slong, traits::fits_into_slong,
330         to.set_nored(nmod_mat_entry(e1._mat(), e2, e3)))
331 
332 FLINT_DEFINE_BINARY_EXPR_COND2(times, nmod_matxx,
333         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
334         nmod_mat_mul(to._mat(), e1._mat(), e2._mat()))
335 FLINT_DEFINE_CBINARY_EXPR_COND2(times, nmod_matxx,
336         NMOD_MATXX_COND_S, NMODXX_COND_S,
337         nmod_mat_scalar_mul(to._mat(), e1._mat(), e2._limb()))
338 
339 FLINT_DEFINE_BINARY_EXPR_COND2(plus, nmod_matxx,
340         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
341         nmod_mat_add(to._mat(), e1._mat(), e2._mat()))
342 FLINT_DEFINE_BINARY_EXPR_COND2(minus, nmod_matxx,
343         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
344         nmod_mat_sub(to._mat(), e1._mat(), e2._mat()))
345 
346 FLINT_DEFINE_UNARY_EXPR_COND(negate, nmod_matxx, NMOD_MATXX_COND_S,
347         nmod_mat_neg(to._mat(), from._mat()))
348 
349 FLINT_DEFINE_UNARY_EXPR_COND(transpose_op, nmod_matxx, NMOD_MATXX_COND_S,
350         nmod_mat_transpose(to._mat(), from._mat()))
351 FLINT_DEFINE_UNARY_EXPR_COND(trace_op, nmodxx, NMOD_MATXX_COND_S,
352         to.set_nored(nmod_mat_trace(from._mat())))
353 
354 FLINT_DEFINE_BINARY_EXPR_COND2(mul_classical_op, nmod_matxx,
355         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
356         nmod_mat_mul(to._mat(), e1._mat(), e2._mat()))
357 FLINT_DEFINE_BINARY_EXPR_COND2(mul_strassen_op, nmod_matxx,
358         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
359         nmod_mat_mul(to._mat(), e1._mat(), e2._mat()))
360 
361 FLINT_DEFINE_UNARY_EXPR_COND(det_op, nmodxx, NMOD_MATXX_COND_S,
362         to.set_nored(nmod_mat_det(from._mat())))
363 
364 FLINT_DEFINE_UNARY_EXPR_COND(inv_op, nmod_matxx, NMOD_MATXX_COND_S,
365         execution_check(nmod_mat_inv(to._mat(), from._mat()),
366             "inv", "nmod_mat"))
367 
368 #define NMOD_MATXX_DEFINE_SOLVE_TRI(name) \
369 FLINT_DEFINE_THREEARY_EXPR_COND3(name##_op, nmod_matxx, \
370         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S, tools::is_bool, \
371         nmod_mat_##name(to._mat(), e1._mat(), e2._mat(), e3))
372 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_tril)
373 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_tril_classical)
374 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_tril_recursive)
375 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_triu)
376 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_triu_classical)
377 NMOD_MATXX_DEFINE_SOLVE_TRI(solve_triu_recursive)
378 
379 FLINT_DEFINE_BINARY_EXPR_COND2(solve_op, nmod_matxx,
380         NMOD_MATXX_COND_S, NMOD_MATXX_COND_S,
381         execution_check(nmod_mat_solve(to._mat(), e1._mat(), e2._mat()),
382             "solve", "nmod_mat"))
383 
384 FLINT_DEFINE_BINARY_EXPR_COND2(solve_op, nmod_vecxx,
385         NMOD_MATXX_COND_S, NMOD_VECXX_COND_S,
386         execution_check(nmod_mat_solve_vec(to._array(), e1._mat(), e2._array()),
387             "solve_vec", "nmod_mat"))
388 
389 namespace rdetail {
390 typedef make_ltuple<mp::make_tuple<slong, nmod_matxx>::type >::type
391     nmod_mat_nullspace_rt;
392 } // rdetail
393 FLINT_DEFINE_UNARY_EXPR_COND(nullspace_op, rdetail::nmod_mat_nullspace_rt,
394         NMOD_MATXX_COND_S, to.template get<0>() = nmod_mat_nullspace(
395             to.template get<1>()._mat(), from._mat()))
396 } // rules
397 
398 //////////////////////////////////////////////////////////////////////////////
399 // nmod_mat_vector class
400 //////////////////////////////////////////////////////////////////////////////
401 // This class stores a vector of nmod_matxx with differing moduli. It is *not*
402 // an expression template class!
403 
404 class nmod_mat_vector
405 {
406 private:
407     nmod_mat_t* data;
408     std::size_t size_;
409 
410     void init(const nmod_mat_vector& o)
411     {
412         size_ = o.size_;
413         data = new nmod_mat_t[size_];
414         for(std::size_t i = 0;i < size_;++i)
415             nmod_mat_init_set(data[i], o.data[i]);
416     }
417 
418     void clear(void)
419     {
420         for(std::size_t i = 0;i < size_;++i)
421             nmod_mat_clear(data[i]);
422         delete[] data;
423     }
424 
425 public:
426     ~nmod_mat_vector() {clear();}
427     nmod_mat_vector(slong rows, slong cols, const std::vector<mp_limb_t>& primes)
428     {
429         size_ = primes.size();
430         data = new nmod_mat_t[primes.size()];
431         for(std::size_t i = 0;i < primes.size();++i)
432             nmod_mat_init(data[i], rows, cols, primes[i]);
433     }
434 
435     nmod_mat_vector(const nmod_mat_vector& o)
436     {
437         init(o);
438     }
439 
440     nmod_mat_vector& operator=(const nmod_mat_vector& o)
441     {
442         clear();
443         init(o);
444         return *this;
445     }
446 
447     nmod_matxx_ref operator[](std::size_t idx)
448         {return nmod_matxx_ref::make(data[idx]);}
449     nmod_matxx_srcref operator[](std::size_t idx) const
450         {return nmod_matxx_srcref::make(data[idx]);}
451 
452     std::size_t size() const {return size_;}
453 
454     const nmod_mat_t* _data() const {return data;}
455     nmod_mat_t* _data() {return data;}
456 
457     bool operator==(const nmod_mat_vector& o)
458     {
459         if(size() != o.size())
460             return false;
461         for(std::size_t i = 0;i < size();++i)
462             if((*this)[i] != o[i])
463                 return false;
464         return true;
465     }
466     bool operator!=(const nmod_mat_vector& o)
467     {
468         return !(*this == o);
469     }
470 
471     template<class Fmpz_mat>
472     void set_multi_mod(const Fmpz_mat& m,
473             typename mp::enable_if<traits::is_fmpz_matxx<Fmpz_mat> >::type* = 0)
474     {
475         fmpz_mat_multi_mod_ui(data, size(), m.evaluate()._mat());
476     }
477     template<class Fmpz_mat>
478     void set_multi_mod_precomp(const Fmpz_mat& m,
479             const fmpz_combxx& comb,
480             typename mp::enable_if<traits::is_fmpz_matxx<Fmpz_mat> >::type* = 0)
481     {
482         fmpz_mat_multi_mod_ui_precomp(data, size(), m.evaluate()._mat(),
483                 comb._comb(), comb._temp());
484     }
485 };
486 
487 /////////////////////////////////////////////////////////////////////////////
488 // chinese remaindering
489 /////////////////////////////////////////////////////////////////////////////
490 // Note this operates on fmpz_matxx and fmpz_combxx (as well as nmod_matxx).
491 // We define it here to deal with the circular dependencies. fmpz_matxx.h
492 // includes nmod_matxx.h at the bottom.
493 
494 template<class Fmpz_mat>
495 inline nmod_mat_vector multi_mod(const Fmpz_mat& m,
496         const std::vector<mp_limb_t>& primes,
497         typename mp::enable_if<traits::is_fmpz_matxx<Fmpz_mat> >::type* = 0)
498 {
499     nmod_mat_vector res(m.rows(), m.cols(), primes);
500     res.set_multi_mod(m);
501     return res;
502 }
503 template<class Fmpz_mat>
504 inline nmod_mat_vector multi_mod_precomp(const Fmpz_mat& m,
505         const std::vector<mp_limb_t>& primes,
506         const fmpz_combxx& comb,
507         typename mp::enable_if<traits::is_fmpz_matxx<Fmpz_mat> >::type* = 0)
508 {
509     nmod_mat_vector res(m.rows(), m.cols(), primes);
510     res.set_multi_mod_precomp(m, comb);
511     return res;
512 }
513 
514 namespace matrices {
515 // outsize computation for multi-CRT
516 struct outsize_CRT
517 {
518     template<class Mat>
519     static slong rows(const Mat& m)
520     {
521         return m._data().first()[0].rows();
522     }
523     template<class Mat>
524     static slong cols(const Mat& m)
525     {
526         return m._data().first()[0].cols();
527     }
528 };
529 
530 template<> struct outsize<operations::multi_CRT_op> : outsize_CRT { };
531 template<> struct outsize<operations::multi_CRT_precomp_op> : outsize_CRT { };
532 }
533 
534 namespace rules {
535 FLINT_DEFINE_FOURARY_EXPR_COND4(CRT_op, fmpz_matxx,
536         FMPZ_MATXX_COND_T, FMPZXX_COND_S, NMOD_MATXX_COND_S, tools::is_bool,
537         fmpz_mat_CRT_ui(to._mat(), e1._mat(), e2._fmpz(), e3._mat(), e4))
538 
539 FLINT_DEFINE_BINARY_EXPR2(multi_CRT_op, fmpz_matxx, nmod_mat_vector, bool,
540         fmpz_mat_multi_CRT_ui(to._mat(), (nmod_mat_t * const) e1._data(), e1.size(), e2))
541 FLINT_DEFINE_THREEARY_EXPR(multi_CRT_precomp_op, fmpz_matxx,
542         nmod_mat_vector, fmpz_combxx, bool,
543         fmpz_mat_multi_CRT_ui_precomp(to._mat(), (nmod_mat_t * const) e1._data(), e1.size(),
544             e2._comb(), e2._temp(), e3))
545 } // rules
546 } // flint
547 
548 #endif
549