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