Skip to main content

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