qfall_math/integer_mod_q/mat_polynomial_ring_zq/
get.rs

1// Copyright © 2023 Marcel Luca Schmidt
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! Implementations to get information about a [`MatPolynomialRingZq`] matrix.
10
11use super::MatPolynomialRingZq;
12use crate::{
13    integer::{MatPolyOverZ, PolyOverZ},
14    integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
15    traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix},
16};
17use flint_sys::{fmpz_poly::fmpz_poly_struct, fmpz_poly_mat::fmpz_poly_mat_entry};
18
19impl MatPolynomialRingZq {
20    /// Returns the modulus of the matrix as a [`ModulusPolynomialRingZq`].
21    ///
22    /// # Examples
23    /// ```
24    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
25    /// use qfall_math::integer::MatPolyOverZ;
26    /// use std::str::FromStr;
27    ///
28    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
29    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
30    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
31    ///
32    /// let modulus = poly_ring_mat.get_mod();
33    /// ```
34    pub fn get_mod(&self) -> ModulusPolynomialRingZq {
35        self.modulus.clone()
36    }
37}
38
39impl MatPolynomialRingZq {
40    /// Creates a [`MatPolyOverZ`] where each entry is a representative of the
41    /// equivalence class of each entry from a [`MatPolynomialRingZq`].
42    ///
43    /// The representation of the coefficients is in the range `[0, modulus)` and
44    /// the representation of the polynomials is in the range `[0, modulus_polynomial)`.
45    ///
46    /// # Examples
47    /// ```
48    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
49    /// use qfall_math::integer::MatPolyOverZ;
50    /// use std::str::FromStr;
51    ///
52    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
53    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
54    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
55    ///
56    /// let matrix = poly_ring_mat.get_representative_least_nonnegative_residue();
57    ///
58    /// let cmp_poly_mat = MatPolyOverZ::from_str("[[3  15 0 1, 1  8],[0, 2  1 2]]").unwrap();
59    /// assert_eq!(cmp_poly_mat, matrix);
60    /// ```
61    pub fn get_representative_least_nonnegative_residue(&self) -> MatPolyOverZ {
62        self.matrix.clone()
63    }
64}
65
66impl MatrixDimensions for MatPolynomialRingZq {
67    /// Returns the number of rows of the matrix as an [`i64`].
68    ///
69    /// # Examples
70    /// ```
71    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
72    /// use qfall_math::integer::MatPolyOverZ;
73    /// use qfall_math::traits::*;
74    /// use std::str::FromStr;
75    ///
76    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
77    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
78    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
79    ///
80    /// let rows = poly_ring_mat.get_num_rows();
81    /// ```
82    fn get_num_rows(&self) -> i64 {
83        self.matrix.get_num_rows()
84    }
85
86    /// Returns the number of columns of the matrix as an [`i64`].
87    ///
88    /// # Examples
89    /// ```
90    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
91    /// use qfall_math::integer::MatPolyOverZ;
92    /// use qfall_math::traits::*;
93    /// use std::str::FromStr;
94    ///
95    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
96    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
97    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
98    ///
99    /// let rows = poly_ring_mat.get_num_columns();
100    /// ```
101    fn get_num_columns(&self) -> i64 {
102        self.matrix.get_num_columns()
103    }
104}
105
106impl MatrixGetEntry<PolyOverZ> for MatPolynomialRingZq {
107    /// Outputs the [`PolyOverZ`] value of a specific matrix entry
108    /// without checking whether it's part of the matrix.
109    ///
110    /// Parameters:
111    /// - `row`: specifies the row in which the entry is located
112    /// - `column`: specifies the column in which the entry is located
113    ///
114    /// Returns the [`PolyOverZ`] value of the matrix at the position of the given
115    /// row and column.
116    ///
117    /// # Safety
118    /// To use this function safely, make sure that the selected entry is part
119    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
120    /// occur.
121    ///
122    /// # Examples
123    /// ```
124    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
125    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
126    /// use qfall_math::traits::*;
127    /// use std::str::FromStr;
128    ///
129    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 50").unwrap();
130    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
131    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
132    ///
133    /// let entry_1: PolyOverZ = unsafe { poly_ring_mat.get_entry_unchecked(1, 0) };
134    /// let entry_2: PolyOverZ = unsafe { poly_ring_mat.get_entry_unchecked(0, 1) };
135    ///
136    ///
137    /// assert_eq!(entry_1, PolyOverZ::from(0));
138    /// assert_eq!(entry_2, PolyOverZ::from(42));
139    /// ```
140    unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> PolyOverZ {
141        unsafe { self.matrix.get_entry_unchecked(row, column) }
142    }
143}
144
145impl MatrixGetEntry<PolynomialRingZq> for MatPolynomialRingZq {
146    /// Outputs the [`PolynomialRingZq`] value of a specific matrix entry
147    /// without checking whether it's part of the matrix.
148    ///
149    /// Parameters:
150    /// - `row`: specifies the row in which the entry is located
151    /// - `column`: specifies the column in which the entry is located
152    ///
153    /// Returns the [`PolynomialRingZq`] value of the matrix at the position of the given
154    /// row and column.
155    ///
156    /// # Safety
157    /// To use this function safely, make sure that the selected entry is part
158    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
159    /// occur.
160    ///
161    /// # Examples
162    /// ```
163    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq};
164    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
165    /// use qfall_math::traits::*;
166    /// use std::str::FromStr;
167    ///
168    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 50").unwrap();
169    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
170    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
171    ///
172    /// let entry_1: PolynomialRingZq = unsafe { poly_ring_mat.get_entry_unchecked(0, 1) };
173    /// let entry_2: PolynomialRingZq = unsafe { poly_ring_mat.get_entry_unchecked(0, 1) };
174    ///
175    /// let value_cmp = PolynomialRingZq::from((&PolyOverZ::from(42), &modulus));
176    /// assert_eq!(entry_1, value_cmp);
177    /// assert_eq!(entry_1, entry_2);
178    /// ```
179    unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> PolynomialRingZq {
180        PolynomialRingZq {
181            poly: unsafe { self.matrix.get_entry_unchecked(row, column) },
182            modulus: self.get_mod(),
183        }
184    }
185}
186
187impl MatrixGetSubmatrix for MatPolynomialRingZq {
188    /// Returns a deep copy of the submatrix defined by the given parameters
189    /// and does not check the provided dimensions.
190    /// There is also a safe version of this function that checks the input.
191    ///
192    /// Parameters:
193    /// `row_1`: the starting row of the submatrix
194    /// `row_2`: the ending row of the submatrix
195    /// `col_1`: the starting column of the submatrix
196    /// `col_2`: the ending column of the submatrix
197    ///
198    /// Returns the submatrix from `(row_1, col_1)` to `(row_2, col_2)`(exclusively).
199    ///
200    /// # Examples
201    /// ```
202    /// use qfall_math::{integer::MatPolyOverZ, traits::MatrixGetSubmatrix};
203    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
204    /// use std::str::FromStr;
205    ///
206    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();();
207    /// let mat = MatPolyOverZ::identity(3, 3);
208    /// let poly_ring_mat = MatPolynomialRingZq::from((&mat, &modulus));
209    ///
210    /// let sub_mat_1 = poly_ring_mat.get_submatrix(0, 2, 1, 1).unwrap();
211    /// let sub_mat_2 = poly_ring_mat.get_submatrix(0, -1, 1, -2).unwrap();
212    /// let sub_mat_3 = unsafe{poly_ring_mat.get_submatrix_unchecked(0, 3, 1, 2)};
213    ///
214    /// let e_2 = MatPolyOverZ::from_str("[[0],[1  1],[0]]").unwrap();
215    /// let e_2 = MatPolynomialRingZq::from((&e_2, &modulus));
216    /// assert_eq!(e_2, sub_mat_1);
217    /// assert_eq!(e_2, sub_mat_2);
218    /// assert_eq!(e_2, sub_mat_3);
219    /// ```
220    ///
221    /// # Safety
222    /// To use this function safely, make sure that the selected submatrix is part
223    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
224    /// occur.
225    unsafe fn get_submatrix_unchecked(
226        &self,
227        row_1: i64,
228        row_2: i64,
229        col_1: i64,
230        col_2: i64,
231    ) -> Self {
232        MatPolynomialRingZq {
233            matrix: unsafe {
234                self.matrix
235                    .get_submatrix_unchecked(row_1, row_2, col_1, col_2)
236            },
237            modulus: self.get_mod(),
238        }
239    }
240}
241
242impl MatPolynomialRingZq {
243    /// Efficiently collects all [`fmpz_poly_struct`]s in a [`MatPolynomialRingZq`] without cloning them.
244    ///
245    /// Hence, the values on the returned [`Vec`] are intended for short-term use
246    /// as the access to [`fmpz_poly_struct`] values could lead to memory leaks or modified values
247    /// once the [`MatPolynomialRingZq`] instance was modified or dropped.
248    ///
249    /// # Examples
250    /// ```compile_fail
251    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
252    /// use qfall_math::integer::MatPolyOverZ;
253    /// use std::str::FromStr;
254    ///
255    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
256    /// let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[2  1 2, 3  1 1 1]]").unwrap();
257    /// let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
258    ///
259    /// let fmpz_entries = poly_ring_mat.collect_entries();
260    /// ```
261    #[allow(dead_code)]
262    pub(crate) fn collect_entries(&self) -> Vec<fmpz_poly_struct> {
263        let mut entries: Vec<fmpz_poly_struct> =
264            Vec::with_capacity((self.get_num_rows() * self.get_num_columns()) as usize);
265
266        for row in 0..self.get_num_rows() {
267            for col in 0..self.get_num_columns() {
268                // efficiently get entry without cloning the entry itself
269                let entry = unsafe { *fmpz_poly_mat_entry(&self.matrix.matrix, row, col) };
270                entries.push(entry);
271            }
272        }
273
274        entries
275    }
276}
277
278#[cfg(test)]
279mod test_get_entry {
280    use crate::integer::{MatPolyOverZ, PolyOverZ};
281    use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq};
282    use crate::traits::MatrixGetEntry;
283    use std::str::FromStr;
284
285    const LARGE_PRIME: u64 = u64::MAX - 58;
286
287    /// Ensure that getting entries works on the edge.
288    #[test]
289    fn get_edges() {
290        let modulus = ModulusPolynomialRingZq::from_str("2  42 17 mod 89").unwrap();
291        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
292
293        let entry_1: PolyOverZ = matrix.get_entry(0, 0).unwrap();
294        let entry_2: PolyOverZ = matrix.get_entry(4, 9).unwrap();
295
296        assert_eq!(PolyOverZ::default(), entry_1);
297        assert_eq!(PolyOverZ::default(), entry_2);
298    }
299
300    /// Ensure that getting entries works with large numbers.
301    #[test]
302    fn large_positive() {
303        let modulus =
304            ModulusPolynomialRingZq::from_str(&format!("5  42 17 1 2 3 mod {LARGE_PRIME}"))
305                .unwrap();
306        let poly_mat =
307            MatPolyOverZ::from_str(&format!("[[4  1 0 {} 1, 1  42],[0, 2  1 2]]", i64::MAX))
308                .unwrap();
309        let matrix = MatPolynomialRingZq::from((&poly_mat, &modulus));
310
311        let entry: PolyOverZ = matrix.get_entry(0, 0).unwrap();
312
313        assert_eq!(
314            PolyOverZ::from_str(&format!("4  1 0 {} 1", i64::MAX)).unwrap(),
315            entry
316        );
317    }
318
319    /// Ensure that a wrong number of rows yields an Error.
320    #[test]
321    fn error_wrong_row() {
322        let modulus =
323            ModulusPolynomialRingZq::from_str(&format!("5  42 17 1 2 3 mod {LARGE_PRIME}"))
324                .unwrap();
325        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
326
327        assert!(MatrixGetEntry::<PolynomialRingZq>::get_entry(&matrix, 5, 1).is_err());
328        assert!(MatrixGetEntry::<PolynomialRingZq>::get_entry(&matrix, -6, 1).is_err());
329        assert!(MatrixGetEntry::<PolyOverZ>::get_entry(&matrix, 5, 1).is_err());
330        assert!(MatrixGetEntry::<PolyOverZ>::get_entry(&matrix, -6, 1).is_err());
331    }
332
333    /// Ensure that a wrong number of columns yields an Error.
334    #[test]
335    fn error_wrong_column() {
336        let modulus =
337            ModulusPolynomialRingZq::from_str(&format!("5  42 17 1 2 3 mod {LARGE_PRIME}"))
338                .unwrap();
339        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
340
341        assert!(MatrixGetEntry::<PolynomialRingZq>::get_entry(&matrix, 1, 10).is_err());
342        assert!(MatrixGetEntry::<PolynomialRingZq>::get_entry(&matrix, 1, -11).is_err());
343        assert!(MatrixGetEntry::<PolyOverZ>::get_entry(&matrix, 1, 10).is_err());
344        assert!(MatrixGetEntry::<PolyOverZ>::get_entry(&matrix, 1, -11).is_err());
345    }
346
347    /// Ensure that getting entries works with different types.
348    #[test]
349    fn diff_types() {
350        let modulus =
351            ModulusPolynomialRingZq::from_str(&format!("5  42 17 1 2 3 mod {LARGE_PRIME}"))
352                .unwrap();
353        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
354
355        let _: PolyOverZ = matrix.get_entry(0, 0).unwrap();
356        let _: PolynomialRingZq = matrix.get_entry(0, 0).unwrap();
357    }
358}
359
360#[cfg(test)]
361mod test_get_num {
362    use crate::{
363        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
364        traits::MatrixDimensions,
365    };
366    use std::str::FromStr;
367
368    /// Ensure that the getter for number of rows works correctly.
369    #[test]
370    fn num_rows() {
371        let modulus = ModulusPolynomialRingZq::from_str("2  42 17 mod 89").unwrap();
372        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
373
374        assert_eq!(matrix.get_num_rows(), 5);
375    }
376
377    /// Ensure that the getter for number of columns works correctly.
378    #[test]
379    fn num_columns() {
380        let modulus = ModulusPolynomialRingZq::from_str("2  42 17 mod 89").unwrap();
381        let matrix = MatPolynomialRingZq::new(5, 10, &modulus);
382
383        assert_eq!(matrix.get_num_columns(), 10);
384    }
385}
386
387#[cfg(test)]
388mod test_mod {
389    use crate::{
390        integer::MatPolyOverZ,
391        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
392    };
393    use std::str::FromStr;
394
395    const LARGE_PRIME: u64 = u64::MAX - 58;
396
397    /// Ensure that the getter for modulus works correctly.
398    #[test]
399    fn get_mod() {
400        let modulus = ModulusPolynomialRingZq::from_str("2  42 17 mod 89").unwrap();
401        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
402        let matrix = MatPolynomialRingZq::from((&poly_mat, &modulus));
403
404        assert_eq!(
405            matrix.get_mod(),
406            ModulusPolynomialRingZq::from_str("2  42 17 mod 89").unwrap()
407        );
408    }
409
410    /// Ensure that the getter for modulus works with large numbers.
411    #[test]
412    fn get_mod_large() {
413        let modulus =
414            ModulusPolynomialRingZq::from_str(&format!("2  42 17 mod {LARGE_PRIME}")).unwrap();
415        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
416        let matrix = MatPolynomialRingZq::from((&poly_mat, &modulus));
417
418        assert_eq!(
419            matrix.get_mod(),
420            ModulusPolynomialRingZq::from_str(&format!("2  42 17 mod {LARGE_PRIME}")).unwrap()
421        );
422    }
423
424    /// Ensure that no memory leak occurs in get_mod.
425    #[test]
426    fn get_mod_memory() {
427        let modulus =
428            ModulusPolynomialRingZq::from_str(&format!("2  42 17 mod {LARGE_PRIME}")).unwrap();
429        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
430        let matrix = MatPolynomialRingZq::from((&poly_mat, &modulus));
431        let _ = matrix.get_mod();
432        let _ = ModulusPolynomialRingZq::from_str(&format!("2  42 17 mod {LARGE_PRIME}")).unwrap();
433
434        let modulus = matrix.get_mod();
435
436        assert_eq!(
437            modulus,
438            ModulusPolynomialRingZq::from_str(&format!("2  42 17 mod {LARGE_PRIME}")).unwrap()
439        );
440    }
441}
442
443#[cfg(test)]
444mod test_get_representative_least_nonnegative_residue {
445    use crate::{
446        integer::MatPolyOverZ,
447        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
448    };
449    use std::str::FromStr;
450
451    const LARGE_PRIME: u64 = u64::MAX - 58;
452
453    /// Ensure that the getter for a large modulus and large entries works.
454    #[test]
455    fn get_representative_least_nonnegative_residue_large_entry_and_modulus() {
456        let modulus =
457            ModulusPolynomialRingZq::from_str(&format!("5  42 0 0 0 1 mod {LARGE_PRIME}")).unwrap();
458        let poly_mat = MatPolyOverZ::from_str("[[4  1 0 0 1, 1  42],[0, 1  -1]]").unwrap();
459        let matrix = MatPolynomialRingZq::from((&poly_mat, &modulus));
460
461        assert_eq!(
462            MatPolyOverZ::from_str(&format!(
463                "[[4  1 0 0 1, 1  42],[0, 1  {}]]",
464                LARGE_PRIME - 1
465            ))
466            .unwrap(),
467            matrix.get_representative_least_nonnegative_residue()
468        )
469    }
470}
471
472#[cfg(test)]
473mod test_get_vec {
474    use crate::{
475        integer::MatPolyOverZ,
476        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
477        traits::MatrixGetSubmatrix,
478    };
479    use std::str::FromStr;
480
481    /// Ensure that getting a row works.
482    #[test]
483    fn get_row_works() {
484        let matrix = MatPolyOverZ::from_str(&format!(
485            "[[0, 0, 0],[1  42, 1  {}, 1  {}]]",
486            i64::MAX,
487            i64::MIN
488        ))
489        .unwrap();
490        let modulus =
491            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
492        let matrix = MatPolynomialRingZq::from((&matrix, &modulus));
493
494        let row_1 = matrix.get_row(0).unwrap();
495        let row_2 = matrix.get_row(1).unwrap();
496
497        let cmp_1 = MatPolyOverZ::from_str("[[0, 0, 0]]").unwrap();
498        let cmp_2 = MatPolyOverZ::from_str(&format!("[[1  42, 1  {}, 1  {}]]", i64::MAX, i64::MIN))
499            .unwrap();
500        let cmp_1 = MatPolynomialRingZq::from((&cmp_1, &modulus));
501        let cmp_2 = MatPolynomialRingZq::from((&cmp_2, &modulus));
502        assert_eq!(cmp_1, row_1);
503        assert_eq!(cmp_2, row_2);
504    }
505
506    /// Ensure that getting a row with a negative index works
507    #[test]
508    fn get_row_negative_indexing_works() {
509        let matrix = MatPolyOverZ::from_str(&format!(
510            "[[0, 0, 0],[1  42, 1  {}, 1  {}]]",
511            i64::MAX,
512            u64::MAX - 1
513        ))
514        .unwrap();
515        let modulus =
516            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
517        let matrix = MatPolynomialRingZq::from((&matrix, &modulus));
518        let row_1 = matrix.get_row(-2).unwrap();
519        let row_2 = matrix.get_row(-1).unwrap();
520
521        let cmp_1 = MatPolyOverZ::from_str("[[0, 0, 0]]").unwrap();
522        let cmp_2 =
523            MatPolyOverZ::from_str(&format!("[[1  42, 1  {}, 1  {}]]", i64::MAX, u64::MAX - 1))
524                .unwrap();
525
526        assert_eq!(cmp_1, row_1.matrix);
527        assert_eq!(cmp_2, row_2.matrix);
528    }
529
530    /// Ensure that getting a column works.
531    #[test]
532    fn get_column_works() {
533        let matrix = MatPolyOverZ::from_str(&format!(
534            "[[1  42, 0, 2  17 42],[1  {}, 0, 2  17 42],[1  {}, 0, 2  17 42]]",
535            i64::MAX,
536            i64::MIN
537        ))
538        .unwrap();
539        let modulus =
540            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
541        let matrix = MatPolynomialRingZq::from((&matrix, &modulus));
542
543        let column_1 = matrix.get_column(0).unwrap();
544        let column_2 = matrix.get_column(1).unwrap();
545        let column_3 = matrix.get_column(2).unwrap();
546
547        let cmp_1 =
548            MatPolyOverZ::from_str(&format!("[[1  42],[1  {}],[1  {}]]", i64::MAX, i64::MIN))
549                .unwrap();
550        let cmp_2 = MatPolyOverZ::from_str("[[0],[0],[0]]").unwrap();
551        let cmp_3 = MatPolyOverZ::from_str("[[2  17 42],[2  17 42],[2  17 42]]").unwrap();
552        let cmp_1 = MatPolynomialRingZq::from((&cmp_1, &modulus));
553        let cmp_2 = MatPolynomialRingZq::from((&cmp_2, &modulus));
554        let cmp_3 = MatPolynomialRingZq::from((&cmp_3, &modulus));
555        assert_eq!(cmp_1, column_1);
556        assert_eq!(cmp_2, column_2);
557        assert_eq!(cmp_3, column_3);
558    }
559
560    /// Ensure that getting a column with a negative index works
561    #[test]
562    fn get_column_negative_indexing_works() {
563        let matrix = MatPolyOverZ::from_str(&format!(
564            "[[1  42, 0, 2  17 42],[1  {}, 0, 2  17 42],[1  {}, 0, 2  17 42]]",
565            i64::MAX,
566            u64::MAX - 1
567        ))
568        .unwrap();
569        let modulus =
570            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
571        let matrix = MatPolynomialRingZq::from((&matrix, &modulus));
572        let column_1 = matrix.get_column(-3).unwrap();
573        let column_2 = matrix.get_column(-2).unwrap();
574        let column_3 = matrix.get_column(-1).unwrap();
575
576        let cmp_1 = MatPolyOverZ::from_str(&format!(
577            "[[1  42],[1  {}],[1  {}]]",
578            i64::MAX,
579            u64::MAX - 1
580        ))
581        .unwrap();
582        let cmp_2 = MatPolyOverZ::from_str("[[0],[0],[0]]").unwrap();
583        let cmp_3 = MatPolyOverZ::from_str("[[2  17 42],[2  17 42],[2  17 42]]").unwrap();
584
585        assert_eq!(cmp_1, column_1.matrix);
586        assert_eq!(cmp_2, column_2.matrix);
587        assert_eq!(cmp_3, column_3.matrix);
588    }
589
590    /// Ensure that wrong row and column dimensions yields an error.
591    #[test]
592    fn wrong_dim_error() {
593        let modulus =
594            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
595        let matrix = MatPolyOverZ::from_str(&format!(
596            "[[1  17, 2  17 42, 3  1 1 1],[1  {}, 1  1, 2  2 3],[1  {}, 1  142, 1  1]]",
597            i64::MAX,
598            i64::MIN
599        ))
600        .unwrap();
601        let matrix = MatPolynomialRingZq::from((&matrix, &modulus));
602
603        let row_1 = matrix.get_row(-4);
604        let row_2 = matrix.get_row(4);
605        let column_1 = matrix.get_column(-4);
606        let column_2 = matrix.get_column(4);
607
608        assert!(row_1.is_err());
609        assert!(row_2.is_err());
610        assert!(column_1.is_err());
611        assert!(column_2.is_err());
612    }
613}
614
615#[cfg(test)]
616mod test_get_submatrix {
617    use crate::{
618        integer::{MatPolyOverZ, Z},
619        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
620        traits::{MatrixDimensions, MatrixGetSubmatrix},
621    };
622    use std::str::FromStr;
623
624    /// Ensures that getting the entire matrix as a submatrix works.
625    #[test]
626    fn entire_matrix() {
627        let modulus =
628            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
629        let mat = MatPolyOverZ::identity(5, 5);
630        let mat = MatPolynomialRingZq::from((&mat, &modulus));
631
632        let sub_mat = mat.get_submatrix(0, 4, 0, 4).unwrap();
633
634        assert_eq!(mat, sub_mat);
635    }
636
637    /// Ensures that a single matrix entry can be retrieved.
638    #[test]
639    fn matrix_single_entry() {
640        let modulus =
641            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
642        let mat = MatPolyOverZ::identity(5, 5);
643        let mat = MatPolynomialRingZq::from((&mat, &modulus));
644
645        let sub_mat = mat.get_submatrix(0, 0, 0, 0).unwrap();
646
647        let cmp_mat = MatPolyOverZ::identity(1, 1);
648        let cmp_mat = MatPolynomialRingZq::from((&cmp_mat, &modulus));
649        assert_eq!(cmp_mat, sub_mat);
650    }
651
652    /// Ensures that the dimensions of the submatrix are correct.
653    #[test]
654    fn correct_dimensions() {
655        let modulus =
656            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
657        let mat = MatPolyOverZ::identity(100, 100);
658        let mat = MatPolynomialRingZq::from((&mat, &modulus));
659
660        let sub_mat = mat.get_submatrix(1, 37, 0, 29).unwrap();
661
662        assert_eq!(37, sub_mat.get_num_rows());
663        assert_eq!(30, sub_mat.get_num_columns());
664    }
665
666    /// Ensures that a submatrix can be correctly retrieved for a matrix with large
667    /// entries.
668    #[test]
669    fn large_entries() {
670        let modulus =
671            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
672        let mat = MatPolyOverZ::from_str(&format!(
673            "[[2  -1 {}, 1  2, 1  3],[1  1, 1  {}, 1  3]]",
674            i64::MAX,
675            i64::MIN
676        ))
677        .unwrap();
678        let mat = MatPolynomialRingZq::from((&mat, &modulus));
679
680        let sub_mat = mat.get_submatrix(0, 1, 0, 1).unwrap();
681
682        let cmp_mat = MatPolyOverZ::from_str(&format!(
683            "[[2  -1 {}, 1  2],[1  1, 1  {}]]",
684            i64::MAX,
685            i64::MIN
686        ))
687        .unwrap();
688        let cmp_mat = MatPolynomialRingZq::from((&cmp_mat, &modulus));
689        assert_eq!(cmp_mat, sub_mat);
690    }
691
692    /// Ensures that an error is returned if coordinates are addressed that are not
693    /// within the matrix.
694    #[test]
695    fn invalid_coordinates() {
696        let modulus =
697            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
698        let mat = MatPolyOverZ::identity(10, 10);
699        let mat = MatPolynomialRingZq::from((&mat, &modulus));
700
701        assert!(mat.get_submatrix(0, 0, 0, 10).is_err());
702        assert!(mat.get_submatrix(0, 10, 0, 0).is_err());
703        assert!(mat.get_submatrix(0, 0, -11, 0).is_err());
704        assert!(mat.get_submatrix(-11, 0, 0, 0).is_err());
705    }
706
707    /// Ensure that negative indices return the correct submatrix.
708    #[test]
709    fn negative_indexing() {
710        let modulus =
711            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
712        let mat = MatPolyOverZ::identity(3, 3);
713        let matrix = MatPolynomialRingZq::from((&mat, &modulus));
714
715        assert_eq!(matrix, matrix.get_submatrix(0, -1, 0, -1).unwrap());
716        assert_eq!(matrix, matrix.get_submatrix(-3, -1, -3, -1).unwrap());
717    }
718
719    /// Ensures that the function panics if no columns of the matrix are addressed.
720    #[test]
721    #[should_panic]
722    fn no_columns() {
723        let modulus =
724            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
725        let mat = MatPolyOverZ::identity(10, 10);
726        let mat = MatPolynomialRingZq::from((&mat, &modulus));
727
728        let _ = mat.get_submatrix(0, 0, 6, 5);
729    }
730
731    /// Ensures that the function panics if no rows of the matrix are addressed.
732    #[test]
733    #[should_panic]
734    fn no_rows() {
735        let modulus =
736            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
737        let mat = MatPolyOverZ::identity(10, 10);
738        let mat = MatPolynomialRingZq::from((&mat, &modulus));
739
740        let _ = mat.get_submatrix(5, 4, 0, 0);
741    }
742
743    /// Ensure that the submatrix function can be called with several types.
744    #[test]
745    fn availability() {
746        let modulus =
747            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
748        let mat = MatPolyOverZ::identity(10, 10);
749        let mat = MatPolynomialRingZq::from((&mat, &modulus));
750
751        let _ = mat.get_submatrix(0_i8, 0_i8, 0_i8, 0_i8);
752        let _ = mat.get_submatrix(0_i16, 0_i16, 0_i16, 0_i16);
753        let _ = mat.get_submatrix(0_i32, 0_i32, 0_i32, 0_i32);
754        let _ = mat.get_submatrix(0_i64, 0_i64, 0_i64, 0_i64);
755        let _ = mat.get_submatrix(0_u8, 0_u8, 0_u8, 0_u8);
756        let _ = mat.get_submatrix(0_u16, 0_i16, 0_u16, 0_u16);
757        let _ = mat.get_submatrix(0_u32, 0_i32, 0_u32, 0_u32);
758        let _ = mat.get_submatrix(0_u64, 0_i64, 0_u64, 0_u64);
759        let _ = mat.get_submatrix(&Z::ZERO, &Z::ZERO, &Z::ZERO, &Z::ZERO);
760    }
761}
762
763#[cfg(test)]
764mod test_collect_entries {
765    use crate::integer::{MatPolyOverZ, PolyOverZ};
766    use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
767    use flint_sys::fmpz_poly::fmpz_poly_set;
768    use std::str::FromStr;
769
770    const LARGE_PRIME: u64 = u64::MAX - 58;
771
772    /// Ensures that all entries of the polynomial are actually collected in the vector.
773    #[test]
774    fn all_entries_collected() {
775        let modulus =
776            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
777        let poly_mat_1 = MatPolyOverZ::from_str(&format!(
778            "[[4  -1 0 3 1, 1  {}],[2  1 2, 3  {} 1 1]]",
779            i64::MAX,
780            i64::MIN + 58,
781        ))
782        .unwrap();
783        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
784        let poly_mat_2 = MatPolyOverZ::from_str("[[1  42, 2  1 17]]").unwrap();
785        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
786
787        let entries_1 = poly_ring_mat_1.collect_entries();
788        let entries_2 = poly_ring_mat_2.collect_entries();
789
790        let mut entry_1 = PolyOverZ::default();
791        let mut entry_2 = entry_1.clone();
792        let mut entry_3 = entry_1.clone();
793
794        unsafe { fmpz_poly_set(&mut entry_1.poly, &entries_1[1]) }
795        unsafe { fmpz_poly_set(&mut entry_2.poly, &entries_1[3]) }
796        unsafe { fmpz_poly_set(&mut entry_3.poly, &entries_2[0]) }
797
798        assert_eq!(entries_1.len(), 4);
799        assert_eq!(
800            PolyOverZ::from_str(&format!("1  {}", i64::MAX)).unwrap(),
801            entry_1
802        );
803        assert_eq!(
804            PolyOverZ::from_str(&format!("3  {} 1 1", i64::MAX)).unwrap(),
805            entry_2
806        );
807
808        assert_eq!(entries_2.len(), 2);
809        assert_eq!(PolyOverZ::from(42), entry_3);
810    }
811
812    /// Ensure that the doc-test compiles and works correctly.
813    #[test]
814    fn doc_test() {
815        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
816        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[2  1 2, 3  1 1 1]]").unwrap();
817        let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
818
819        let _ = poly_ring_mat.collect_entries();
820    }
821}