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