qfall_math/integer_mod_q/mat_polynomial_ring_zq/
concat.rs1use super::MatPolynomialRingZq;
12use crate::{
13 error::MathError,
14 integer::MatPolyOverZ,
15 traits::{CompareBase, Concatenate, MatrixDimensions},
16};
17use flint_sys::fmpz_poly_mat::{fmpz_poly_mat_concat_horizontal, fmpz_poly_mat_concat_vertical};
18
19impl Concatenate for &MatPolynomialRingZq {
20 type Output = MatPolynomialRingZq;
21
22 fn concat_vertical(self, other: Self) -> Result<Self::Output, crate::error::MathError> {
53 if self.get_num_columns() != other.get_num_columns() {
54 return Err(MathError::MismatchingMatrixDimension(format!(
55 "Tried to concatenate vertically a '{}x{}' matrix and a '{}x{}' matrix.",
56 self.get_num_rows(),
57 self.get_num_columns(),
58 other.get_num_rows(),
59 other.get_num_columns()
60 )));
61 }
62
63 if !self.compare_base(other) {
64 return Err(self.call_compare_base_error(other).unwrap());
65 }
66
67 let mut matrix = MatPolyOverZ::new(
68 self.get_num_rows() + other.get_num_rows(),
69 self.get_num_columns(),
70 );
71 unsafe {
72 fmpz_poly_mat_concat_vertical(
73 &mut matrix.matrix,
74 &self.matrix.matrix,
75 &other.matrix.matrix,
76 );
77 }
78 Ok(MatPolynomialRingZq {
79 matrix,
80 modulus: self.get_mod(),
81 })
82 }
83
84 fn concat_horizontal(self, other: Self) -> Result<Self::Output, crate::error::MathError> {
115 if self.get_num_rows() != other.get_num_rows() {
116 return Err(MathError::MismatchingMatrixDimension(format!(
117 "Tried to concatenate horizontally a '{}x{}' matrix and a '{}x{}' matrix.",
118 self.get_num_rows(),
119 self.get_num_columns(),
120 other.get_num_rows(),
121 other.get_num_columns()
122 )));
123 }
124
125 if !self.compare_base(other) {
126 return Err(self.call_compare_base_error(other).unwrap());
127 }
128
129 let mut matrix = MatPolyOverZ::new(
130 self.get_num_rows(),
131 self.get_num_columns() + other.get_num_columns(),
132 );
133 unsafe {
134 fmpz_poly_mat_concat_horizontal(
135 &mut matrix.matrix,
136 &self.matrix.matrix,
137 &other.matrix.matrix,
138 );
139 }
140 Ok(MatPolynomialRingZq {
141 matrix,
142 modulus: self.get_mod(),
143 })
144 }
145}
146
147#[cfg(test)]
148mod test_concatenate {
149 use crate::{
150 integer::MatPolyOverZ,
151 integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
152 traits::{Concatenate, MatrixDimensions},
153 };
154 use std::str::FromStr;
155
156 const LARGE_PRIME: u64 = u64::MAX - 58;
157
158 #[test]
161 fn dimensions_vertical() {
162 let modulus_str = format!("3 1 {} 1 mod {LARGE_PRIME}", i64::MAX);
163 let modulus = ModulusPolynomialRingZq::from_str(&modulus_str).unwrap();
164 let mat_1 = MatPolynomialRingZq::new(13, 5, &modulus);
165 let mat_2 = MatPolynomialRingZq::new(17, 5, &modulus);
166 let mat_3 = MatPolynomialRingZq::new(17, 6, &modulus);
167
168 let mat_vert = mat_1.concat_vertical(&mat_2).unwrap();
169
170 assert!(mat_1.concat_vertical(&mat_3).is_err());
171
172 assert_eq!(5, mat_vert.get_num_columns());
173 assert_eq!(30, mat_vert.get_num_rows());
174 }
175
176 #[test]
179 fn dimensions_horizontal() {
180 let modulus_str = format!("3 1 {} 1 mod {LARGE_PRIME}", i64::MAX);
181 let modulus = ModulusPolynomialRingZq::from_str(&modulus_str).unwrap();
182 let mat_1 = MatPolynomialRingZq::new(13, 5, &modulus);
183 let mat_2 = MatPolynomialRingZq::new(17, 5, &modulus);
184 let mat_3 = MatPolynomialRingZq::new(17, 6, &modulus);
185
186 let mat_vert = mat_2.concat_horizontal(&mat_3).unwrap();
187
188 assert!(mat_1.concat_horizontal(&mat_2).is_err());
189
190 assert_eq!(11, mat_vert.get_num_columns());
191 assert_eq!(17, mat_vert.get_num_rows());
192 }
193
194 #[test]
197 fn mismatching_moduli() {
198 let mat_1 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0]] / 2 1 1 mod 6").unwrap();
199 let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0]] / 2 1 1 mod 7").unwrap();
200
201 let mat_hor = mat_1.concat_horizontal(&mat_2);
202 let mat_vert = mat_1.concat_vertical(&mat_2);
203
204 assert!(mat_hor.is_err());
205 assert!(mat_vert.is_err());
206 }
207
208 #[test]
210 fn vertically_correct() {
211 let modulus_str = format!("3 1 {} 1 mod {LARGE_PRIME}", i64::MAX);
212 let modulus = ModulusPolynomialRingZq::from_str(&modulus_str).unwrap();
213 let poly_mat_1 =
214 MatPolyOverZ::from_str(&format!("[[4 2 {} 1 1, 1 42],[0, 2 1 2]]", u64::MAX))
215 .unwrap();
216 let poly_mat_2 = MatPolyOverZ::from_str("[[1 27, 2 10 5]]").unwrap();
217 let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
218 let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
219
220 let mat_vertical = poly_ring_mat_1.concat_vertical(&poly_ring_mat_2).unwrap();
221
222 let poly_mat_cmp =
223 MatPolyOverZ::from_str("[[4 2 58 1 1, 1 42],[0, 2 1 2],[1 27, 2 10 5]]").unwrap();
224 let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
225
226 assert_eq!(poly_ring_mat_cmp, mat_vertical);
227 }
228
229 #[test]
231 fn horizontally_correct() {
232 let modulus_str = format!("3 1 {} 1 mod {LARGE_PRIME}", i64::MAX);
233 let modulus = ModulusPolynomialRingZq::from_str(&modulus_str).unwrap();
234 let poly_mat_1 = MatPolyOverZ::from_str(&format!(
235 "[[4 {} {} 1 1, 1 42],[0, 2 1 2]]",
236 LARGE_PRIME + 2,
237 u64::MAX
238 ))
239 .unwrap();
240 let poly_mat_2 = MatPolyOverZ::from_str("[[1 27],[2 10 5]]").unwrap();
241 let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
242 let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
243
244 let mat_vertical = poly_ring_mat_1.concat_horizontal(&poly_ring_mat_2).unwrap();
245
246 let poly_mat_cmp =
247 MatPolyOverZ::from_str("[[4 2 58 1 1, 1 42, 1 27],[0, 2 1 2, 2 10 5]]").unwrap();
248 let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
249
250 assert_eq!(poly_ring_mat_cmp, mat_vertical);
251 }
252}