1use super::MatPolyOverZ;
12use crate::{
13 integer::PolyOverZ,
14 traits::{MatrixDimensions, MatrixGetEntry, Tensor},
15};
16use flint_sys::{fmpz_poly::fmpz_poly_mul, fmpz_poly_mat::fmpz_poly_mat_entry};
17
18impl Tensor for MatPolyOverZ {
19 fn tensor_product(&self, other: &Self) -> Self {
44 let mut out = MatPolyOverZ::new(
45 self.get_num_rows() * other.get_num_rows(),
46 self.get_num_columns() * other.get_num_columns(),
47 );
48
49 for i in 0..self.get_num_rows() {
50 for j in 0..self.get_num_columns() {
51 let entry = unsafe { self.get_entry_unchecked(i, j) };
52
53 if !entry.is_zero() {
54 unsafe { set_matrix_window_mul(&mut out, i, j, entry, other) }
55 }
56 }
57 }
58
59 out
60 }
61}
62
63unsafe fn set_matrix_window_mul(
89 out: &mut MatPolyOverZ,
90 row_left: i64,
91 column_upper: i64,
92 scalar: PolyOverZ,
93 matrix: &MatPolyOverZ,
94) {
95 let columns_other = matrix.get_num_columns();
96 let rows_other = matrix.get_num_rows();
97
98 assert!(row_left >= 0 && row_left + rows_other <= out.get_num_rows());
99 assert!(column_upper >= 0 && column_upper + columns_other <= out.get_num_columns());
100
101 for i_other in 0..rows_other {
102 for j_other in 0..columns_other {
103 unsafe {
104 fmpz_poly_mul(
105 fmpz_poly_mat_entry(
106 &out.matrix,
107 row_left * rows_other + i_other,
108 column_upper * columns_other + j_other,
109 ),
110 &scalar.poly,
111 fmpz_poly_mat_entry(&matrix.matrix, i_other, j_other),
112 )
113 }
114 }
115 }
116}
117
118#[cfg(test)]
119mod test_tensor {
120 use crate::{
121 integer::MatPolyOverZ,
122 traits::{MatrixDimensions, Tensor},
123 };
124 use std::str::FromStr;
125
126 #[test]
128 fn dimensions_fit() {
129 let mat_1 = MatPolyOverZ::new(17, 13);
130 let mat_2 = MatPolyOverZ::new(3, 4);
131
132 let mat_3 = mat_1.tensor_product(&mat_2);
133
134 assert_eq!(51, mat_3.get_num_rows());
135 assert_eq!(52, mat_3.get_num_columns());
136 }
137
138 #[test]
140 fn identity() {
141 let identity = MatPolyOverZ::identity(2, 2);
142 let mat_1 = MatPolyOverZ::from_str(&format!(
143 "[[1 1, 1 {}, 1 1],[0, 1 {}, 1 -1]]",
144 u64::MAX,
145 i64::MIN
146 ))
147 .unwrap();
148
149 let mat_2 = identity.tensor_product(&mat_1);
150 let mat_3 = mat_1.tensor_product(&identity);
151
152 let cmp_mat_2 = MatPolyOverZ::from_str(&format!(
153 "[[1 1, 1 {}, 1 1, 0, 0, 0],[0, 1 {}, 1 -1, 0, 0, 0],[0, 0, 0, 1 1, 1 {}, 1 1],[0, 0, 0, 0, 1 {}, 1 -1]]",
154 u64::MAX,
155 i64::MIN,
156 u64::MAX,
157 i64::MIN
158 ))
159 .unwrap();
160 let cmp_mat_3 = MatPolyOverZ::from_str(&format!(
161 "[[1 1, 0, 1 {}, 0, 1 1, 0],[0, 1 1, 0, 1 {}, 0, 1 1],[0, 0, 1 {}, 0, 1 -1, 0],[0, 0, 0, 1 {}, 0, 1 -1]]",
162 u64::MAX,
163 u64::MAX,
164 i64::MIN,
165 i64::MIN
166 ))
167 .unwrap();
168
169 assert_eq!(cmp_mat_2, mat_2);
170 assert_eq!(cmp_mat_3, mat_3);
171 }
172
173 #[test]
175 fn vector_matrix() {
176 let vector = MatPolyOverZ::from_str("[[1 1],[1 -1]]").unwrap();
177 let mat_1 = MatPolyOverZ::from_str(&format!(
178 "[[1 1, 1 {}, 1 1],[0, 1 {}, 1 -1]]",
179 u64::MAX,
180 i64::MAX
181 ))
182 .unwrap();
183
184 let mat_2 = vector.tensor_product(&mat_1);
185 let mat_3 = mat_1.tensor_product(&vector);
186
187 let cmp_mat_2 = MatPolyOverZ::from_str(&format!(
188 "[[1 1, 1 {}, 1 1],[0, 1 {}, 1 -1],[1 -1, 1 -{}, 1 -1],[0, 1 -{}, 1 1]]",
189 u64::MAX,
190 i64::MAX,
191 u64::MAX,
192 i64::MAX
193 ))
194 .unwrap();
195 let cmp_mat_3 = MatPolyOverZ::from_str(&format!(
196 "[[1 1, 1 {}, 1 1],[1 -1, 1 -{}, 1 -1],[0, 1 {}, 1 -1],[0, 1 -{}, 1 1]]",
197 u64::MAX,
198 u64::MAX,
199 i64::MAX,
200 i64::MAX
201 ))
202 .unwrap();
203
204 assert_eq!(cmp_mat_2, mat_2);
205 assert_eq!(cmp_mat_3, mat_3);
206 }
207
208 #[test]
210 fn vector_vector() {
211 let vec_1 = MatPolyOverZ::from_str("[[1 2],[1 1]]").unwrap();
212 let vec_2 = MatPolyOverZ::from_str(&format!(
213 "[[1 {}],[1 {}]]",
214 (u64::MAX - 1) / 2,
215 i64::MIN / 2
216 ))
217 .unwrap();
218
219 let vec_3 = vec_1.tensor_product(&vec_2);
220 let vec_4 = vec_2.tensor_product(&vec_1);
221
222 let cmp_vec_3 = MatPolyOverZ::from_str(&format!(
223 "[[1 {}],[1 {}],[1 {}],[1 {}]]",
224 u64::MAX - 1,
225 i64::MIN,
226 (u64::MAX - 1) / 2,
227 i64::MIN / 2
228 ))
229 .unwrap();
230 let cmp_vec_4 = MatPolyOverZ::from_str(&format!(
231 "[[1 {}],[1 {}],[1 {}],[1 {}]]",
232 u64::MAX - 1,
233 (u64::MAX - 1) / 2,
234 i64::MIN,
235 i64::MIN / 2
236 ))
237 .unwrap();
238
239 assert_eq!(cmp_vec_3, vec_3);
240 assert_eq!(cmp_vec_4, vec_4);
241 }
242
243 #[test]
245 fn higher_degree() {
246 let higher_degree = MatPolyOverZ::from_str("[[1 1, 2 0 1, 2 1 1]]").unwrap();
247 let mat_1 =
248 MatPolyOverZ::from_str(&format!("[[1 1, 1 {}, 2 1 {}]]", u64::MAX, i64::MIN))
249 .unwrap();
250
251 let mat_2 = higher_degree.tensor_product(&mat_1);
252
253 let cmp_mat_2 = MatPolyOverZ::from_str(&format!(
254 "[[1 1, 1 {}, 2 1 {}, 2 0 1, 2 0 {}, 3 0 1 {}, 2 1 1, 2 {} {}, 3 1 {} {}]]",
255 u64::MAX,
256 i64::MIN,
257 u64::MAX,
258 i64::MIN,
259 u64::MAX,
260 u64::MAX,
261 i64::MIN + 1,
262 i64::MIN,
263 ))
264 .unwrap();
265
266 assert_eq!(cmp_mat_2, mat_2);
267 }
268}