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 flint3_sys::fmpz_mat_add;
20use flint3_sys::{_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 {
65 fmpz_mod_mat_add(
66 &mut self.matrix,
67 &self.matrix,
68 &other.matrix,
69 self.modulus.get_fmpz_mod_ctx_struct(),
70 )
71 };
72 }
73}
74impl AddAssign<&MatZ> for MatZq {
75 fn add_assign(&mut self, other: &MatZ) {
77 if self.get_num_rows() != other.get_num_rows()
78 || self.get_num_columns() != other.get_num_columns()
79 {
80 panic!(
81 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
82 self.get_num_rows(),
83 self.get_num_columns(),
84 other.get_num_rows(),
85 other.get_num_columns()
86 );
87 }
88
89 unsafe {
90 fmpz_mat_add(&mut self.matrix, &self.matrix, &other.matrix);
91 _fmpz_mod_mat_reduce(&mut self.matrix, self.modulus.get_fmpz_mod_ctx_struct());
92 };
93 }
94}
95
96arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZq);
97arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZ);
98
99impl Add for &MatZq {
100 type Output = MatZq;
101 fn add(self, other: Self) -> Self::Output {
127 self.add_safe(other).unwrap()
128 }
129}
130
131impl Add<&MatZ> for &MatZq {
132 type Output = MatZq;
133
134 fn add(self, other: &MatZ) -> Self::Output {
159 if self.get_num_rows() != other.get_num_rows()
160 || self.get_num_columns() != other.get_num_columns()
161 {
162 panic!(
163 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
164 self.get_num_rows(),
165 self.get_num_columns(),
166 other.get_num_rows(),
167 other.get_num_columns()
168 );
169 }
170
171 let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
172 unsafe {
173 fmpz_mat_add(&mut out.matrix, &self.matrix, &other.matrix);
174 _fmpz_mod_mat_reduce(&mut out.matrix, self.modulus.get_fmpz_mod_ctx_struct());
175 }
176 out
177 }
178}
179
180arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZ, MatZq);
181arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZ, MatZq);
182arithmetic_trait_reverse!(Add, add, MatZ, MatZq, MatZq);
183arithmetic_trait_borrowed_to_owned!(Add, add, MatZ, MatZq, MatZq);
184arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZ, MatZq, MatZq);
185
186impl MatZq {
187 pub fn add_safe(&self, other: &Self) -> Result<MatZq, MathError> {
212 if !self.compare_base(other) {
213 return Err(self.call_compare_base_error(other).unwrap());
214 }
215 if self.get_num_rows() != other.get_num_rows()
216 || self.get_num_columns() != other.get_num_columns()
217 {
218 return Err(MathError::MismatchingMatrixDimension(format!(
219 "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
220 self.get_num_rows(),
221 self.get_num_columns(),
222 other.get_num_rows(),
223 other.get_num_columns()
224 )));
225 }
226 let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
227 unsafe {
228 fmpz_mod_mat_add(
229 &mut out.matrix,
230 &self.matrix,
231 &other.matrix,
232 self.modulus.get_fmpz_mod_ctx_struct(),
233 );
234 }
235 Ok(out)
236 }
237}
238
239arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZq, MatZq);
240arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZq, MatZq);
241
242#[cfg(test)]
243mod test_add_assign {
244 use crate::{integer::MatZ, integer_mod_q::MatZq};
245 use std::str::FromStr;
246
247 #[test]
249 fn correct_small() {
250 let mut a = MatZq::identity(2, 2, 7);
251 let b = MatZq::from_str("[[4, 5],[-6, 6]] mod 7").unwrap();
252 let mut c = a.clone();
253 let d = b.get_representative_least_nonnegative_residue();
254 let cmp = MatZq::from_str("[[5, 5],[1, 0]] mod 7").unwrap();
255
256 a += b;
257 c += d;
258
259 assert_eq!(cmp, a);
260 assert_eq!(cmp, c);
261 }
262
263 #[test]
265 fn correct_large() {
266 let mut a = MatZq::from_str(&format!(
267 "[[{}, 5],[{}, -1]] mod {}",
268 i64::MAX,
269 i64::MIN,
270 u64::MAX
271 ))
272 .unwrap();
273 let b = MatZq::from_str(&format!("[[{}, -6],[6, -1]] mod {}", i64::MAX, u64::MAX)).unwrap();
274 let cmp = MatZq::from_str(&format!(
275 "[[{}, -1],[{}, -2]] mod {}",
276 2 * (i64::MAX as u64),
277 i64::MIN + 6,
278 u64::MAX
279 ))
280 .unwrap();
281
282 a += b;
283
284 assert_eq!(cmp, a);
285 }
286
287 #[test]
289 fn matrix_dimensions() {
290 let dimensions = [(3, 3), (5, 1), (1, 4)];
291
292 for (nr_rows, nr_cols) in dimensions {
293 let mut a = MatZq::new(nr_rows, nr_cols, 7);
294 let b = MatZq::identity(nr_rows, nr_cols, 7);
295
296 a += b;
297
298 assert_eq!(MatZq::identity(nr_rows, nr_cols, 7), a);
299 }
300 }
301
302 #[test]
304 #[should_panic]
305 fn mismatching_moduli() {
306 let mut a = MatZq::new(1, 1, 5);
307 let b = MatZq::new(1, 1, 6);
308
309 a += b;
310 }
311
312 #[test]
314 fn availability() {
315 let mut a = MatZq::new(2, 2, 7);
316 let b = MatZq::new(2, 2, 7);
317 let c = MatZ::new(2, 2);
318
319 a += &b;
320 a += b;
321 a += &c;
322 a += c;
323 }
324}
325
326#[cfg(test)]
327mod test_add {
328 use super::MatZq;
329 use std::str::FromStr;
330
331 #[test]
333 fn add() {
334 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
335 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
336 let c: MatZq = a + b;
337 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
338 }
339
340 #[test]
342 fn add_borrow() {
343 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
344 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
345 let c: MatZq = &a + &b;
346 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
347 }
348
349 #[test]
351 fn add_first_borrowed() {
352 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
353 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
354 let c: MatZq = &a + b;
355 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
356 }
357
358 #[test]
360 fn add_second_borrowed() {
361 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
362 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
363 let c: MatZq = a + &b;
364 assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
365 }
366
367 #[test]
369 fn add_large_numbers() {
370 let a: MatZq = MatZq::from_str(&format!(
371 "[[1, 2, {}],[3, -4, {}]] mod {}",
372 i64::MIN,
373 i128::MAX,
374 u64::MAX - 58
375 ))
376 .unwrap();
377 let b: MatZq = MatZq::from_str(&format!(
378 "[[1, 2, {}],[3, 9, {}]] mod {}",
379 i64::MIN + 1,
380 i128::MAX,
381 u64::MAX - 58
382 ))
383 .unwrap();
384 let c: MatZq = a + &b;
385 assert_eq!(
386 c,
387 MatZq::from_str(&format!(
388 "[[2, 4, -{}],[6, 5, {}]] mod {}",
389 u64::MAX,
390 u128::MAX - 1,
391 u64::MAX - 58
392 ))
393 .unwrap()
394 );
395 }
396
397 #[test]
399 fn add_safe() {
400 let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 4").unwrap();
401 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 4").unwrap();
402 let c = a.add_safe(&b);
403 assert_eq!(
404 c.unwrap(),
405 MatZq::from_str("[[2, 0, 2],[2, 3, 2]] mod 4").unwrap()
406 );
407 }
408
409 #[test]
411 fn add_safe_is_err() {
412 let a: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 4").unwrap();
413 let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -4, 5]] mod 4").unwrap();
414 let c: MatZq = MatZq::from_str("[[1, 2, 3]] mod 4").unwrap();
415 let d: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 7").unwrap();
416 assert!(a.add_safe(&b).is_err());
417 assert!(c.add_safe(&b).is_err());
418 assert!(a.add_safe(&d).is_err());
419 }
420}
421
422#[cfg(test)]
423mod test_add_matz {
424 use super::MatZq;
425 use crate::integer::MatZ;
426 use std::str::FromStr;
427
428 #[test]
430 fn small_numbers() {
431 let a = MatZ::from_str("[[1, 2],[3, 4]]").unwrap();
432 let b = MatZq::from_str("[[5, 6],[9, 10]] mod 11").unwrap();
433 let cmp = MatZq::from_str("[[6, 8],[1, 3]] mod 11").unwrap();
434
435 let res_0 = &a + &b;
436 let res_1 = &b + &a;
437
438 assert_eq!(res_0, res_1);
439 assert_eq!(cmp, res_0);
440 }
441
442 #[test]
444 fn large_numbers() {
445 let a: MatZ =
446 MatZ::from_str(&format!("[[1, 2, {}],[3, -4, {}]]", i64::MIN, i128::MAX,)).unwrap();
447 let b: MatZq = MatZq::from_str(&format!(
448 "[[1, 2, {}],[3, 9, {}]] mod {}",
449 i64::MIN + 1,
450 i128::MAX,
451 u64::MAX - 58
452 ))
453 .unwrap();
454
455 let c = a + &b;
456
457 assert_eq!(
458 c,
459 MatZq::from_str(&format!(
460 "[[2, 4, -{}],[6, 5, {}]] mod {}",
461 u64::MAX,
462 u128::MAX - 1,
463 u64::MAX - 58
464 ))
465 .unwrap()
466 );
467 }
468
469 #[test]
471 fn available() {
472 let a = MatZ::new(2, 2);
473 let b = MatZq::new(2, 2, 7);
474
475 let _ = &b + &a;
476 let _ = &b + a.clone();
477 let _ = b.clone() + &a;
478 let _ = b.clone() + a.clone();
479 let _ = &a + &b;
480 let _ = &a + b.clone();
481 let _ = a.clone() + &b;
482 }
483
484 #[test]
486 #[should_panic]
487 fn mismatching_rows() {
488 let a = MatZ::new(3, 2);
489 let b = MatZq::new(2, 2, 7);
490
491 let _ = &b + &a;
492 }
493
494 #[test]
496 #[should_panic]
497 fn mismatching_column() {
498 let a = MatZ::new(2, 3);
499 let b = MatZq::new(2, 2, 7);
500
501 let _ = &b + &a;
502 }
503}