1use super::super::MatZq;
12use crate::error::MathError;
13use crate::integer::MatZ;
14use crate::macros::arithmetics::{
15 arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
16 arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
17};
18use crate::traits::{CompareBase, MatrixDimensions};
19use flint_sys::fmpz_mat::fmpz_mat_add;
20use flint_sys::fmpz_mod_mat::{_fmpz_mod_mat_reduce, fmpz_mod_mat_add};
21use std::ops::{Add, AddAssign};
22
23impl AddAssign<&MatZq> for MatZq {
24 fn add_assign(&mut self, other: &Self) {
49 if self.get_num_rows() != other.get_num_rows()
50 || self.get_num_columns() != other.get_num_columns()
51 {
52 panic!(
53 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
54 self.get_num_rows(),
55 self.get_num_columns(),
56 other.get_num_rows(),
57 other.get_num_columns()
58 );
59 }
60 if !self.compare_base(other) {
61 panic!("{}", self.call_compare_base_error(other).unwrap());
62 }
63
64 unsafe { fmpz_mod_mat_add(&mut self.matrix, &self.matrix, &other.matrix) };
65 }
66}
67impl AddAssign<&MatZ> for MatZq {
68 fn add_assign(&mut self, other: &MatZ) {
70 if self.get_num_rows() != other.get_num_rows()
71 || self.get_num_columns() != other.get_num_columns()
72 {
73 panic!(
74 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
75 self.get_num_rows(),
76 self.get_num_columns(),
77 other.get_num_rows(),
78 other.get_num_columns()
79 );
80 }
81
82 unsafe {
83 fmpz_mat_add(&mut self.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
84 _fmpz_mod_mat_reduce(&mut self.matrix);
85 };
86 }
87}
88
89arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZq);
90arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZ);
91
92impl Add for &MatZq {
93 type Output = MatZq;
94 fn add(self, other: Self) -> Self::Output {
120 self.add_safe(other).unwrap()
121 }
122}
123
124impl Add<&MatZ> for &MatZq {
125 type Output = MatZq;
126
127 fn add(self, other: &MatZ) -> Self::Output {
152 if self.get_num_rows() != other.get_num_rows()
153 || self.get_num_columns() != other.get_num_columns()
154 {
155 panic!(
156 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
157 self.get_num_rows(),
158 self.get_num_columns(),
159 other.get_num_rows(),
160 other.get_num_columns()
161 );
162 }
163
164 let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
165 unsafe {
166 fmpz_mat_add(&mut out.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
167 _fmpz_mod_mat_reduce(&mut out.matrix);
168 }
169 out
170 }
171}
172
173arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZ, MatZq);
174arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZ, MatZq);
175arithmetic_trait_reverse!(Add, add, MatZ, MatZq, MatZq);
176arithmetic_trait_borrowed_to_owned!(Add, add, MatZ, MatZq, MatZq);
177arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZ, MatZq, MatZq);
178
179impl MatZq {
180 pub fn add_safe(&self, other: &Self) -> Result<MatZq, MathError> {
205 if !self.compare_base(other) {
206 return Err(self.call_compare_base_error(other).unwrap());
207 }
208 if self.get_num_rows() != other.get_num_rows()
209 || self.get_num_columns() != other.get_num_columns()
210 {
211 return Err(MathError::MismatchingMatrixDimension(format!(
212 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
213 self.get_num_rows(),
214 self.get_num_columns(),
215 other.get_num_rows(),
216 other.get_num_columns()
217 )));
218 }
219 let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
220 unsafe {
221 fmpz_mod_mat_add(&mut out.matrix, &self.matrix, &other.matrix);
222 }
223 Ok(out)
224 }
225}
226
227arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZq, MatZq);
228arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZq, MatZq);
229
230#[cfg(test)]
231mod test_add_assign {
232 use crate::{integer::MatZ, integer_mod_q::MatZq};
233 use std::str::FromStr;
234
235 #[test]
237 fn correct_small() {
238 let mut a = MatZq::identity(2, 2, 7);
239 let b = MatZq::from_str("[[4, 5],[-6, 6]] mod 7").unwrap();
240 let mut c = a.clone();
241 let d = b.get_representative_least_nonnegative_residue();
242 let cmp = MatZq::from_str("[[5, 5],[1, 0]] mod 7").unwrap();
243
244 a += b;
245 c += d;
246
247 assert_eq!(cmp, a);
248 assert_eq!(cmp, c);
249 }
250
251 #[test]
253 fn correct_large() {
254 let mut a = MatZq::from_str(&format!(
255 "[[{}, 5],[{}, -1]] mod {}",
256 i64::MAX,
257 i64::MIN,
258 u64::MAX
259 ))
260 .unwrap();
261 let b = MatZq::from_str(&format!("[[{}, -6],[6, -1]] mod {}", i64::MAX, u64::MAX)).unwrap();
262 let cmp = MatZq::from_str(&format!(
263 "[[{}, -1],[{}, -2]] mod {}",
264 2 * (i64::MAX as u64),
265 i64::MIN + 6,
266 u64::MAX
267 ))
268 .unwrap();
269
270 a += b;
271
272 assert_eq!(cmp, a);
273 }
274
275 #[test]
277 fn matrix_dimensions() {
278 let dimensions = [(3, 3), (5, 1), (1, 4)];
279
280 for (nr_rows, nr_cols) in dimensions {
281 let mut a = MatZq::new(nr_rows, nr_cols, 7);
282 let b = MatZq::identity(nr_rows, nr_cols, 7);
283
284 a += b;
285
286 assert_eq!(MatZq::identity(nr_rows, nr_cols, 7), a);
287 }
288 }
289
290 #[test]
292 #[should_panic]
293 fn mismatching_moduli() {
294 let mut a = MatZq::new(1, 1, 5);
295 let b = MatZq::new(1, 1, 6);
296
297 a += b;
298 }
299
300 #[test]
302 fn availability() {
303 let mut a = MatZq::new(2, 2, 7);
304 let b = MatZq::new(2, 2, 7);
305 let c = MatZ::new(2, 2);
306
307 a += &b;
308 a += b;
309 a += &c;
310 a += c;
311 }
312}
313
314#[cfg(test)]
315mod test_add {
316 use super::MatZq;
317 use std::str::FromStr;
318
319 #[test]
321 fn add() {
322 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
323 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
324 let c: MatZq = a + b;
325 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
326 }
327
328 #[test]
330 fn add_borrow() {
331 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
332 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
333 let c: MatZq = &a + &b;
334 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
335 }
336
337 #[test]
339 fn add_first_borrowed() {
340 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
341 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
342 let c: MatZq = &a + b;
343 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
344 }
345
346 #[test]
348 fn add_second_borrowed() {
349 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
350 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
351 let c: MatZq = a + &b;
352 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
353 }
354
355 #[test]
357 fn add_large_numbers() {
358 let a: MatZq = MatZq::from_str(&format!(
359 "[[1, 2, {}],[3, -4, {}]] mod {}",
360 i64::MIN,
361 i128::MAX,
362 u64::MAX - 58
363 ))
364 .unwrap();
365 let b: MatZq = MatZq::from_str(&format!(
366 "[[1, 2, {}],[3, 9, {}]] mod {}",
367 i64::MIN + 1,
368 i128::MAX,
369 u64::MAX - 58
370 ))
371 .unwrap();
372 let c: MatZq = a + &b;
373 assert_eq!(
374 c,
375 MatZq::from_str(&format!(
376 "[[2, 4, -{}],[6, 5, {}]] mod {}",
377 u64::MAX,
378 u128::MAX - 1,
379 u64::MAX - 58
380 ))
381 .unwrap()
382 );
383 }
384
385 #[test]
387 fn add_safe() {
388 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 4").unwrap();
389 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 4").unwrap();
390 let c = a.add_safe(&b);
391 assert_eq!(
392 c.unwrap(),
393 MatZq::from_str("[[2, 0, 2],[2, 3, 2]] mod 4").unwrap()
394 );
395 }
396
397 #[test]
399 fn add_safe_is_err() {
400 let a: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 4").unwrap();
401 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -4, 5]] mod 4").unwrap();
402 let c: MatZq = MatZq::from_str("[[1, 2, 3]] mod 4").unwrap();
403 let d: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 7").unwrap();
404 assert!(a.add_safe(&b).is_err());
405 assert!(c.add_safe(&b).is_err());
406 assert!(a.add_safe(&d).is_err());
407 }
408}
409
410#[cfg(test)]
411mod test_add_matz {
412 use super::MatZq;
413 use crate::integer::MatZ;
414 use std::str::FromStr;
415
416 #[test]
418 fn small_numbers() {
419 let a = MatZ::from_str("[[1, 2],[3, 4]]").unwrap();
420 let b = MatZq::from_str("[[5, 6],[9, 10]] mod 11").unwrap();
421 let cmp = MatZq::from_str("[[6, 8],[1, 3]] mod 11").unwrap();
422
423 let res_0 = &a + &b;
424 let res_1 = &b + &a;
425
426 assert_eq!(res_0, res_1);
427 assert_eq!(cmp, res_0);
428 }
429
430 #[test]
432 fn large_numbers() {
433 let a: MatZ =
434 MatZ::from_str(&format!("[[1, 2, {}],[3, -4, {}]]", i64::MIN, i128::MAX,)).unwrap();
435 let b: MatZq = MatZq::from_str(&format!(
436 "[[1, 2, {}],[3, 9, {}]] mod {}",
437 i64::MIN + 1,
438 i128::MAX,
439 u64::MAX - 58
440 ))
441 .unwrap();
442
443 let c = a + &b;
444
445 assert_eq!(
446 c,
447 MatZq::from_str(&format!(
448 "[[2, 4, -{}],[6, 5, {}]] mod {}",
449 u64::MAX,
450 u128::MAX - 1,
451 u64::MAX - 58
452 ))
453 .unwrap()
454 );
455 }
456
457 #[test]
459 fn available() {
460 let a = MatZ::new(2, 2);
461 let b = MatZq::new(2, 2, 7);
462
463 let _ = &b + &a;
464 let _ = &b + a.clone();
465 let _ = b.clone() + &a;
466 let _ = b.clone() + a.clone();
467 let _ = &a + &b;
468 let _ = &a + b.clone();
469 let _ = a.clone() + &b;
470 }
471
472 #[test]
474 #[should_panic]
475 fn mismatching_rows() {
476 let a = MatZ::new(3, 2);
477 let b = MatZq::new(2, 2, 7);
478
479 let _ = &b + &a;
480 }
481
482 #[test]
484 #[should_panic]
485 fn mismatching_column() {
486 let a = MatZ::new(2, 3);
487 let b = MatZq::new(2, 2, 7);
488
489 let _ = &b + &a;
490 }
491}