use super::super::MatZq;
use crate::error::MathError;
use crate::integer::MatZ;
use crate::macros::arithmetics::{
arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
};
use crate::traits::{CompareBase, MatrixDimensions};
use flint_sys::fmpz_mat::fmpz_mat_add;
use flint_sys::fmpz_mod_mat::{_fmpz_mod_mat_reduce, fmpz_mod_mat_add};
use std::ops::{Add, AddAssign};
impl AddAssign<&MatZq> for MatZq {
fn add_assign(&mut self, other: &Self) {
if self.get_num_rows() != other.get_num_rows()
|| self.get_num_columns() != other.get_num_columns()
{
panic!(
"Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
self.get_num_rows(),
self.get_num_columns(),
other.get_num_rows(),
other.get_num_columns()
);
}
if !self.compare_base(other) {
panic!("{}", self.call_compare_base_error(other).unwrap());
}
unsafe { fmpz_mod_mat_add(&mut self.matrix, &self.matrix, &other.matrix) };
}
}
impl AddAssign<&MatZ> for MatZq {
fn add_assign(&mut self, other: &MatZ) {
if self.get_num_rows() != other.get_num_rows()
|| self.get_num_columns() != other.get_num_columns()
{
panic!(
"Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
self.get_num_rows(),
self.get_num_columns(),
other.get_num_rows(),
other.get_num_columns()
);
}
unsafe {
fmpz_mat_add(&mut self.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
_fmpz_mod_mat_reduce(&mut self.matrix);
};
}
}
arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZq);
arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatZq, MatZ);
impl Add for &MatZq {
type Output = MatZq;
fn add(self, other: Self) -> Self::Output {
self.add_safe(other).unwrap()
}
}
impl Add<&MatZ> for &MatZq {
type Output = MatZq;
fn add(self, other: &MatZ) -> Self::Output {
if self.get_num_rows() != other.get_num_rows()
|| self.get_num_columns() != other.get_num_columns()
{
panic!(
"Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
self.get_num_rows(),
self.get_num_columns(),
other.get_num_rows(),
other.get_num_columns()
);
}
let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
unsafe {
fmpz_mat_add(&mut out.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
_fmpz_mod_mat_reduce(&mut out.matrix);
}
out
}
}
arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZ, MatZq);
arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZ, MatZq);
arithmetic_trait_reverse!(Add, add, MatZ, MatZq, MatZq);
arithmetic_trait_borrowed_to_owned!(Add, add, MatZ, MatZq, MatZq);
arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZ, MatZq, MatZq);
impl MatZq {
pub fn add_safe(&self, other: &Self) -> Result<MatZq, MathError> {
if !self.compare_base(other) {
return Err(self.call_compare_base_error(other).unwrap());
}
if self.get_num_rows() != other.get_num_rows()
|| self.get_num_columns() != other.get_num_columns()
{
return Err(MathError::MismatchingMatrixDimension(format!(
"Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
self.get_num_rows(),
self.get_num_columns(),
other.get_num_rows(),
other.get_num_columns()
)));
}
let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
unsafe {
fmpz_mod_mat_add(&mut out.matrix, &self.matrix, &other.matrix);
}
Ok(out)
}
}
arithmetic_trait_borrowed_to_owned!(Add, add, MatZq, MatZq, MatZq);
arithmetic_trait_mixed_borrowed_owned!(Add, add, MatZq, MatZq, MatZq);
#[cfg(test)]
mod test_add_assign {
use crate::{integer::MatZ, integer_mod_q::MatZq};
use std::str::FromStr;
#[test]
fn correct_small() {
let mut a = MatZq::identity(2, 2, 7);
let b = MatZq::from_str("[[4, 5],[-6, 6]] mod 7").unwrap();
let mut c = a.clone();
let d = b.get_representative_least_nonnegative_residue();
let cmp = MatZq::from_str("[[5, 5],[1, 0]] mod 7").unwrap();
a += b;
c += d;
assert_eq!(cmp, a);
assert_eq!(cmp, c);
}
#[test]
fn correct_large() {
let mut a = MatZq::from_str(&format!(
"[[{}, 5],[{}, -1]] mod {}",
i64::MAX,
i64::MIN,
u64::MAX
))
.unwrap();
let b = MatZq::from_str(&format!("[[{}, -6],[6, -1]] mod {}", i64::MAX, u64::MAX)).unwrap();
let cmp = MatZq::from_str(&format!(
"[[{}, -1],[{}, -2]] mod {}",
2 * (i64::MAX as u64),
i64::MIN + 6,
u64::MAX
))
.unwrap();
a += b;
assert_eq!(cmp, a);
}
#[test]
fn matrix_dimensions() {
let dimensions = [(3, 3), (5, 1), (1, 4)];
for (nr_rows, nr_cols) in dimensions {
let mut a = MatZq::new(nr_rows, nr_cols, 7);
let b = MatZq::identity(nr_rows, nr_cols, 7);
a += b;
assert_eq!(MatZq::identity(nr_rows, nr_cols, 7), a);
}
}
#[test]
#[should_panic]
fn mismatching_moduli() {
let mut a = MatZq::new(1, 1, 5);
let b = MatZq::new(1, 1, 6);
a += b;
}
#[test]
fn availability() {
let mut a = MatZq::new(2, 2, 7);
let b = MatZq::new(2, 2, 7);
let c = MatZ::new(2, 2);
a += &b;
a += b;
a += &c;
a += c;
}
}
#[cfg(test)]
mod test_add {
use super::MatZq;
use std::str::FromStr;
#[test]
fn add() {
let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
let c: MatZq = a + b;
assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
}
#[test]
fn add_borrow() {
let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
let c: MatZq = &a + &b;
assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
}
#[test]
fn add_first_borrowed() {
let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
let c: MatZq = &a + b;
assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
}
#[test]
fn add_second_borrowed() {
let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 7").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 7").unwrap();
let c: MatZq = a + &b;
assert_eq!(c, MatZq::from_str("[[2, 4, 6],[6, 6, 3]] mod 7").unwrap());
}
#[test]
fn add_large_numbers() {
let a: MatZq = MatZq::from_str(&format!(
"[[1, 2, {}],[3, -4, {}]] mod {}",
i64::MIN,
i128::MAX,
u64::MAX - 58
))
.unwrap();
let b: MatZq = MatZq::from_str(&format!(
"[[1, 2, {}],[3, 9, {}]] mod {}",
i64::MIN + 1,
i128::MAX,
u64::MAX - 58
))
.unwrap();
let c: MatZq = a + &b;
assert_eq!(
c,
MatZq::from_str(&format!(
"[[2, 4, -{}],[6, 5, {}]] mod {}",
u64::MAX,
u128::MAX - 1,
u64::MAX - 58
))
.unwrap()
);
}
#[test]
fn add_safe() {
let a: MatZq = MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 4").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -5, 5]] mod 4").unwrap();
let c = a.add_safe(&b);
assert_eq!(
c.unwrap(),
MatZq::from_str("[[2, 0, 2],[2, 3, 2]] mod 4").unwrap()
);
}
#[test]
fn add_safe_is_err() {
let a: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 4").unwrap();
let b: MatZq = MatZq::from_str("[[1, 2, 3],[3, -4, 5]] mod 4").unwrap();
let c: MatZq = MatZq::from_str("[[1, 2, 3]] mod 4").unwrap();
let d: MatZq = MatZq::from_str("[[1, 2],[3, 4]] mod 7").unwrap();
assert!(a.add_safe(&b).is_err());
assert!(c.add_safe(&b).is_err());
assert!(a.add_safe(&d).is_err());
}
}
#[cfg(test)]
mod test_add_matz {
use super::MatZq;
use crate::integer::MatZ;
use std::str::FromStr;
#[test]
fn small_numbers() {
let a = MatZ::from_str("[[1, 2],[3, 4]]").unwrap();
let b = MatZq::from_str("[[5, 6],[9, 10]] mod 11").unwrap();
let cmp = MatZq::from_str("[[6, 8],[1, 3]] mod 11").unwrap();
let res_0 = &a + &b;
let res_1 = &b + &a;
assert_eq!(res_0, res_1);
assert_eq!(cmp, res_0);
}
#[test]
fn large_numbers() {
let a: MatZ =
MatZ::from_str(&format!("[[1, 2, {}],[3, -4, {}]]", i64::MIN, i128::MAX,)).unwrap();
let b: MatZq = MatZq::from_str(&format!(
"[[1, 2, {}],[3, 9, {}]] mod {}",
i64::MIN + 1,
i128::MAX,
u64::MAX - 58
))
.unwrap();
let c = a + &b;
assert_eq!(
c,
MatZq::from_str(&format!(
"[[2, 4, -{}],[6, 5, {}]] mod {}",
u64::MAX,
u128::MAX - 1,
u64::MAX - 58
))
.unwrap()
);
}
#[test]
fn available() {
let a = MatZ::new(2, 2);
let b = MatZq::new(2, 2, 7);
let _ = &b + &a;
let _ = &b + a.clone();
let _ = b.clone() + &a;
let _ = b.clone() + a.clone();
let _ = &a + &b;
let _ = &a + b.clone();
let _ = a.clone() + &b;
}
#[test]
#[should_panic]
fn mismatching_rows() {
let a = MatZ::new(3, 2);
let b = MatZq::new(2, 2, 7);
let _ = &b + &a;
}
#[test]
#[should_panic]
fn mismatching_column() {
let a = MatZ::new(2, 3);
let b = MatZq::new(2, 2, 7);
let _ = &b + &a;
}
}