qfall_math/integer_mod_q/mat_zq/arithmetic/
mul.rs1use super::super::MatZq;
12use crate::error::MathError;
13use crate::integer::MatZ;
14use crate::macros::arithmetics::{
15 arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
16};
17use crate::traits::{CompareBase, MatrixDimensions};
18use flint_sys::fmpz_mat::fmpz_mat_mul;
19use flint_sys::fmpz_mod_mat::{_fmpz_mod_mat_reduce, fmpz_mod_mat_mul};
20use std::ops::Mul;
21
22impl Mul for &MatZq {
23 type Output = MatZq;
24
25 fn mul(self, other: Self) -> Self::Output {
51 self.mul_safe(other).unwrap()
52 }
53}
54
55arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZq, MatZq, MatZq);
56arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZq, MatZq, MatZq);
57
58impl Mul<&MatZ> for &MatZq {
59 type Output = MatZq;
60
61 fn mul(self, other: &MatZ) -> Self::Output {
87 assert_eq!(
88 self.get_num_columns(),
89 other.get_num_rows(),
90 "Tried to multiply matrices with mismatching matrix dimensions."
91 );
92
93 let mut new = MatZq::new(self.get_num_rows(), other.get_num_columns(), self.get_mod());
94 unsafe {
95 fmpz_mat_mul(&mut new.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
96 _fmpz_mod_mat_reduce(&mut new.matrix)
97 }
98 new
99 }
100}
101
102arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZq, MatZ, MatZq);
103arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZq, MatZ, MatZq);
104
105impl MatZq {
106 pub fn mul_safe(&self, other: &Self) -> Result<Self, MathError> {
132 if !self.compare_base(other) {
133 return Err(self.call_compare_base_error(other).unwrap());
134 }
135 if self.get_num_columns() != other.get_num_rows() {
136 return Err(MathError::MismatchingMatrixDimension(format!(
137 "Tried to multiply a '{}x{}' matrix and a '{}x{}' matrix.",
138 self.get_num_rows(),
139 self.get_num_columns(),
140 other.get_num_rows(),
141 other.get_num_columns()
142 )));
143 }
144
145 let mut new = MatZq::new(self.get_num_rows(), other.get_num_columns(), self.get_mod());
146 unsafe { fmpz_mod_mat_mul(&mut new.matrix, &self.matrix, &other.matrix) };
147 Ok(new)
148 }
149}
150
151#[cfg(test)]
152mod test_mul {
153 use super::MatZq;
154 use crate::{integer::Z, traits::MatrixSetEntry};
155 use std::str::FromStr;
156
157 #[test]
159 fn square_correctness() {
160 let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
161 let mat_2 = MatZq::from_str("[[1, 0],[0, 1]] mod 3").unwrap();
162 let mat_3 = MatZq::from_str("[[1, 2],[2, 1]] mod 3").unwrap();
163 let cmp = MatZq::from_str("[[4, 5],[2, 4]] mod 3").unwrap();
164
165 assert_eq!(mat_1, &mat_1 * &mat_2);
166 assert_eq!(cmp, &mat_1 * &mat_3);
167 }
168
169 #[test]
171 fn different_dimensions_correctness() {
172 let mat = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
173 let vec = MatZq::from_str("[[2],[0]] mod 3").unwrap();
174 let cmp = MatZq::from_str("[[1],[2]] mod 3").unwrap();
175
176 assert_eq!(cmp, &mat * &vec);
177 }
178
179 #[test]
181 fn large_entries() {
182 let mat =
183 MatZq::from_str(&format!("[[{}, 1],[0, 2]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
184 let vec = MatZq::from_str(&format!("[[{}],[0]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
185 let mut cmp = MatZq::new(2, 1, u64::MAX - 58);
186 let max: Z = u64::MAX.into();
187 cmp.set_entry(0, 0, &(&max * &max)).unwrap();
188
189 assert_eq!(cmp, mat * vec);
190 }
191
192 #[test]
195 fn errors() {
196 let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 4").unwrap();
197 let mat_2 = MatZq::from_str("[[1, 0],[0, 1],[0, 0]] mod 4").unwrap();
198 let mat_3 = MatZq::from_str("[[2, 1],[1, 2]] mod 5").unwrap();
199 assert!((mat_1.mul_safe(&mat_2)).is_err());
200 assert!((mat_1.mul_safe(&mat_3)).is_err());
201 }
202}
203
204#[cfg(test)]
205mod test_mul_matz {
206 use super::MatZq;
207 use crate::integer::MatZ;
208 use crate::{integer::Z, traits::MatrixSetEntry};
209 use std::str::FromStr;
210
211 #[test]
213 fn square_correctness() {
214 let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
215 let mat_2 = MatZ::identity(2, 2);
216 let mat_3 = MatZ::from_str("[[1, 2],[2, 1]]").unwrap();
217 let cmp = MatZq::from_str("[[4, 5],[2, 4]] mod 3").unwrap();
218
219 assert_eq!(mat_1, &mat_1 * &mat_2);
220 assert_eq!(cmp, &mat_1 * &mat_3);
221 }
222
223 #[test]
225 fn different_dimensions_correctness() {
226 let mat = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
227 let vec = MatZ::from_str("[[2],[0]]").unwrap();
228 let cmp = MatZq::from_str("[[1],[2]] mod 3").unwrap();
229
230 assert_eq!(cmp, &mat * &vec);
231 }
232
233 #[test]
235 fn large_entries() {
236 let mat =
237 MatZq::from_str(&format!("[[{}, 1],[0, 2]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
238 let vec = MatZ::from_str(&format!("[[{}],[0]]", u64::MAX)).unwrap();
239 let mut cmp = MatZq::new(2, 1, u64::MAX - 58);
240 let max: Z = u64::MAX.into();
241 cmp.set_entry(0, 0, &(&max * &max)).unwrap();
242
243 assert_eq!(cmp, &mat * &vec);
244 }
245
246 #[test]
249 #[should_panic]
250 fn errors() {
251 let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 4").unwrap();
252 let mat_2 = MatZ::from_str("[[1, 0],[0, 1],[0, 0]]").unwrap();
253 let _ = &mat_1 * &mat_2;
254 }
255}