Skip to main content

qfall_math/integer_mod_q/mat_polynomial_ring_zq/
set.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//! Implementation to set elements of a [`MatPolynomialRingZq`] matrix.
10
11use super::MatPolynomialRingZq;
12use crate::integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolynomialRingZq};
13use crate::macros::for_others::implement_for_owned;
14use crate::traits::{MatrixSetSubmatrix, MatrixSwaps};
15use crate::{error::MathError, integer::PolyOverZ, traits::MatrixSetEntry};
16use flint3_sys::{
17    fmpz_poly_mat_entry, fmpz_poly_mat_set, fmpz_poly_mat_window_clear, fmpz_poly_mat_window_init,
18    fmpz_poly_set,
19};
20use std::fmt::Display;
21use std::mem::MaybeUninit;
22
23impl MatrixSetEntry<&PolyOverZ> for MatPolynomialRingZq {
24    /// Sets the value of a specific matrix entry according to a given `value` of type [`PolyOverZ`].
25    ///
26    /// Parameters:
27    /// - `row`: specifies the row in which the entry is located
28    /// - `column`: specifies the column in which the entry is located
29    /// - `value`: specifies the value to which the entry is set
30    ///
31    /// Negative indices can be used to index from the back, e.g., `-1` for
32    /// the last element.
33    ///
34    /// Returns an empty `Ok` if the action could be performed successfully.
35    /// Otherwise, a [`MathError`] is returned if the specified entry is
36    /// not part of the matrix.
37    ///
38    /// # Examples
39    /// ```
40    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
41    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
42    /// use crate::qfall_math::traits::*;
43    /// use std::str::FromStr;
44    ///
45    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
46    /// let poly_mat = MatPolyOverZ::from_str("[[0, 1  42],[0, 2  1 2]]").unwrap();
47    /// let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
48    /// let value = PolyOverZ::default();
49    ///
50    /// poly_ring_mat.set_entry(0, 1, &value).unwrap();
51    /// poly_ring_mat.set_entry(-1, -1, &value).unwrap();
52    ///
53    /// let mat_cmp = MatPolynomialRingZq::from((&MatPolyOverZ::new(2, 2), &modulus));
54    /// assert_eq!(poly_ring_mat, mat_cmp);
55    /// ```
56    ///
57    /// # Errors and Failures
58    /// - Returns a [`MathError`] of type [`MathError::OutOfBounds`]
59    ///   if `row` or `column` are greater than the matrix size.
60    fn set_entry(
61        &mut self,
62        row: impl TryInto<i64> + Display,
63        column: impl TryInto<i64> + Display,
64        value: &PolyOverZ,
65    ) -> Result<(), MathError> {
66        let value = PolynomialRingZq::from((value, self.get_mod()));
67
68        self.set_entry(row, column, value)
69    }
70
71    /// Sets the value of a specific matrix entry according to a given `value` of type [`PolyOverZ`]
72    /// without checking whether the coordinate is part of the matrix, if the moduli match
73    /// or if the entry is reduced.
74    ///
75    /// Parameters:
76    /// - `row`: specifies the row in which the entry is located
77    /// - `column`: specifies the column in which the entry is located
78    /// - `value`: specifies the value to which the entry is set
79    ///
80    /// # Safety
81    /// To use this function safely, make sure that the selected entry is part
82    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
83    /// occur.
84    ///
85    /// # Examples
86    /// ```
87    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
88    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
89    /// use crate::qfall_math::traits::*;
90    /// use std::str::FromStr;
91    ///
92    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
93    /// let poly_mat = MatPolyOverZ::from_str("[[0, 1  42],[0, 2  1 2]]").unwrap();
94    /// let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
95    /// let value = PolyOverZ::default();
96    ///
97    /// unsafe {
98    ///     poly_ring_mat.set_entry_unchecked(0, 1, &value);
99    ///     poly_ring_mat.set_entry_unchecked(1, 1, &value);
100    /// }
101    ///
102    /// let mat_cmp = MatPolynomialRingZq::from((&MatPolyOverZ::new(2, 2), &modulus));
103    /// assert_eq!(poly_ring_mat, mat_cmp);
104    /// ```
105    unsafe fn set_entry_unchecked(&mut self, row: i64, column: i64, value: &PolyOverZ) {
106        unsafe {
107            let entry = fmpz_poly_mat_entry(&self.matrix.matrix, row, column);
108            fmpz_poly_set(entry, &value.poly)
109        };
110    }
111}
112
113impl MatrixSetEntry<&PolynomialRingZq> for MatPolynomialRingZq {
114    /// Sets the value of a specific matrix entry according to a given `value` of type [`PolynomialRingZq`]
115    /// without checking whether the coordinate is part of the matrix or if the moduli match.
116    ///
117    /// Parameters:
118    /// - `row`: specifies the row in which the entry is located
119    /// - `column`: specifies the column in which the entry is located
120    /// - `value`: specifies the value to which the entry is set
121    ///
122    /// # Safety
123    /// To use this function safely, make sure that the selected entry is part
124    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
125    /// occur.
126    ///
127    /// # Examples
128    /// ```
129    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq};
130    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
131    /// use crate::qfall_math::traits::*;
132    /// use std::str::FromStr;
133    ///
134    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
135    /// let poly_mat = MatPolyOverZ::from_str("[[0, 1  42],[0, 2  1 2]]").unwrap();
136    /// let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
137    /// let value = PolynomialRingZq::from((&PolyOverZ::default(), &modulus));
138    ///
139    /// unsafe {
140    ///     poly_ring_mat.set_entry_unchecked(0, 1, &value);
141    ///     poly_ring_mat.set_entry_unchecked(1, 1, &value);
142    /// }
143    ///
144    /// let mat_cmp = MatPolynomialRingZq::from((&MatPolyOverZ::new(2, 2), &modulus));
145    /// assert_eq!(poly_ring_mat, mat_cmp);
146    /// ```
147    unsafe fn set_entry_unchecked(&mut self, row: i64, column: i64, value: &PolynomialRingZq) {
148        unsafe {
149            let entry = fmpz_poly_mat_entry(&self.matrix.matrix, row, column);
150            fmpz_poly_set(entry, &value.poly.poly)
151        };
152    }
153}
154
155implement_for_owned!(PolyOverZ, MatPolynomialRingZq, MatrixSetEntry);
156implement_for_owned!(PolynomialRingZq, MatPolynomialRingZq, MatrixSetEntry);
157
158impl MatrixSetSubmatrix for MatPolynomialRingZq {
159    /// Sets the matrix entries in `self` to entries defined in `other`.
160    /// The entries in `self` starting from `(row_self_start, col_self_start)` up to
161    /// `(row_self_end, col_self_end)`are set to be
162    /// the entries from the submatrix from `other` defined by `(row_other_start, col_other_start)`
163    /// to `(row_other_end, col_other_end)` (exclusively).
164    ///
165    /// Parameters:
166    /// `row_self_start`: the starting row of the matrix in which to set a submatrix
167    /// `col_self_start`: the starting column of the matrix in which to set a submatrix
168    /// `other`: the matrix from where to take the submatrix to set
169    /// `row_other_start`: the starting row of the specified submatrix
170    /// `col_other_start`: the starting column of the specified submatrix
171    /// `row_other_end`: the ending row of the specified submatrix
172    /// `col_other_end`:the ending column of the specified submatrix
173    ///
174    /// # Examples
175    /// ```
176    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
177    /// use qfall_math::integer::MatPolyOverZ;
178    /// use qfall_math::traits::MatrixSetSubmatrix;
179    /// use std::str::FromStr;
180    ///
181    /// let mat = MatPolyOverZ::identity(3, 3);
182    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
183    /// let mut mat = MatPolynomialRingZq::from((&mat, &modulus));
184    ///
185    /// mat.set_submatrix(0, 1, &mat.clone(), 0, 0, 1, 1).unwrap();
186    /// // [[1,1,0],[0,0,1],[0,0,1]]
187    /// let mat_cmp = MatPolyOverZ::from_str("[[1  1, 1  1, 0],[0, 0, 1  1],[0, 0, 1  1]]").unwrap();
188    /// assert_eq!(mat, MatPolynomialRingZq::from((&mat_cmp, &modulus)));
189    ///
190    /// unsafe{ mat.set_submatrix_unchecked(2, 0, 3, 2, &mat.clone(), 0, 0, 1, 2) };
191    /// let mat_cmp = MatPolyOverZ::from_str("[[1  1, 1  1, 0],[0, 0, 1  1],[1  1, 1  1, 1  1]]").unwrap();
192    /// assert_eq!(mat, MatPolynomialRingZq::from((&mat_cmp, &modulus)));
193    /// ```
194    ///
195    /// # Safety
196    /// To use this function safely, make sure that the selected submatrices are part
197    /// of the matrices, the submatrices are of the same dimensions and the base types are the same.
198    /// If not, memory leaks, unexpected panics, etc. might occur.
199    unsafe fn set_submatrix_unchecked(
200        &mut self,
201        row_self_start: i64,
202        col_self_start: i64,
203        row_self_end: i64,
204        col_self_end: i64,
205        other: &Self,
206        row_other_start: i64,
207        col_other_start: i64,
208        row_other_end: i64,
209        col_other_end: i64,
210    ) {
211        {
212            let mut window_self = MaybeUninit::uninit();
213            // The memory for the elements of window is shared with self.
214            unsafe {
215                fmpz_poly_mat_window_init(
216                    window_self.as_mut_ptr(),
217                    &self.matrix.matrix,
218                    row_self_start,
219                    col_self_start,
220                    row_self_end,
221                    col_self_end,
222                )
223            };
224            let mut window_other = MaybeUninit::uninit();
225            // The memory for the elements of window is shared with other.
226            unsafe {
227                fmpz_poly_mat_window_init(
228                    window_other.as_mut_ptr(),
229                    &other.matrix.matrix,
230                    row_other_start,
231                    col_other_start,
232                    row_other_end,
233                    col_other_end,
234                )
235            };
236            unsafe {
237                // TODO: this should not be mutable for the other window
238                fmpz_poly_mat_set(window_self.as_mut_ptr(), window_other.as_mut_ptr());
239
240                // Clears the matrix window and releases any memory that it uses. Note that
241                // the memory to the underlying matrix that window points to is not freed
242                fmpz_poly_mat_window_clear(window_self.as_mut_ptr());
243                fmpz_poly_mat_window_clear(window_other.as_mut_ptr());
244            }
245        }
246    }
247}
248
249impl MatrixSwaps for MatPolynomialRingZq {
250    /// Swaps two entries of the specified matrix.
251    ///
252    /// Parameters:
253    /// - `row_0`: specifies the row, in which the first entry is located
254    /// - `col_0`: specifies the column, in which the first entry is located
255    /// - `row_1`: specifies the row, in which the second entry is located
256    /// - `col_1`: specifies the column, in which the second entry is located
257    ///
258    /// Negative indices can be used to index from the back, e.g., `-1` for
259    /// the last element.
260    ///
261    /// Returns an empty `Ok` if the action could be performed successfully.
262    /// Otherwise, a [`MathError`] is returned if one of the specified entries is not part of the matrix.
263    ///
264    /// # Examples
265    /// ```
266    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
267    /// use qfall_math::traits::MatrixSwaps;
268    /// use std::str::FromStr;
269    ///
270    /// let mut matrix = MatPolynomialRingZq::new(4, 3, ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap());
271    /// matrix.swap_entries(0, 0, 2, 1);
272    /// ```
273    ///
274    /// # Errors and Failures
275    /// - Returns a [`MathError`] of type [`MathError::OutOfBounds`]
276    ///   if row or column are greater than the matrix size.
277    fn swap_entries(
278        &mut self,
279        row_0: impl TryInto<i64> + Display,
280        col_0: impl TryInto<i64> + Display,
281        row_1: impl TryInto<i64> + Display,
282        col_1: impl TryInto<i64> + Display,
283    ) -> Result<(), MathError> {
284        self.matrix.swap_entries(row_0, col_0, row_1, col_1)
285    }
286
287    /// Swaps two columns of the specified matrix.
288    ///
289    /// Parameters:
290    /// - `col_0`: specifies the first column which is swapped with the second one
291    /// - `col_1`: specifies the second column which is swapped with the first one
292    ///
293    /// Negative indices can be used to index from the back, e.g., `-1` for
294    /// the last element.
295    ///
296    /// Returns an empty `Ok` if the action could be performed successfully.
297    /// Otherwise, a [`MathError`] is returned if one of the specified columns is not part of the matrix.
298    ///
299    /// # Examples
300    /// ```
301    /// use qfall_math::traits::MatrixSwaps;
302    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
303    /// use std::str::FromStr;
304    ///
305    /// let mut matrix = MatPolynomialRingZq::new(4, 3, ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap());
306    /// matrix.swap_columns(0, 2);
307    /// ```
308    ///
309    /// # Errors and Failures
310    /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds)
311    ///   if one of the given columns is not in the matrix.
312    fn swap_columns(
313        &mut self,
314        col_0: impl TryInto<i64> + Display,
315        col_1: impl TryInto<i64> + Display,
316    ) -> Result<(), MathError> {
317        self.matrix.swap_columns(col_0, col_1)
318    }
319
320    /// Swaps two rows of the specified matrix.
321    ///
322    /// Parameters:
323    /// - `row_0`: specifies the first row which is swapped with the second one
324    /// - `row_1`: specifies the second row which is swapped with the first one
325    ///
326    /// Negative indices can be used to index from the back, e.g., `-1` for
327    /// the last element.
328    ///
329    /// Returns an empty `Ok` if the action could be performed successfully.
330    /// Otherwise, a [`MathError`] is returned if one of the specified rows is not part of the matrix.
331    ///
332    /// # Examples
333    /// ```
334    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
335    /// use qfall_math::traits::MatrixSwaps;
336    /// use std::str::FromStr;
337    ///
338    /// let mut matrix = MatPolynomialRingZq::new(4, 3, ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap());
339    /// matrix.swap_rows(0, 2);
340    /// ```
341    ///
342    /// # Errors and Failures
343    /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds)
344    ///   if one of the given rows is not in the matrix.
345    fn swap_rows(
346        &mut self,
347        row_0: impl TryInto<i64> + Display,
348        row_1: impl TryInto<i64> + Display,
349    ) -> Result<(), MathError> {
350        self.matrix.swap_rows(row_0, row_1)
351    }
352}
353
354impl MatPolynomialRingZq {
355    /// Swaps the `i`-th column with the `n-i`-th column for all `i <= n/2`
356    /// of the specified matrix with `n` columns.
357    ///
358    /// # Examples
359    /// ```
360    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
361    /// use std::str::FromStr;
362    ///
363    /// let mut matrix = MatPolynomialRingZq::new(4, 3, ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap());
364    /// matrix.reverse_columns();
365    /// ```
366    pub fn reverse_columns(&mut self) {
367        self.matrix.reverse_columns()
368    }
369
370    /// Swaps the `i`-th row with the `n-i`-th row for all `i <= n/2`
371    /// of the specified matrix with `n` rows.
372    ///
373    /// # Examples
374    /// ```
375    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
376    /// use std::str::FromStr;
377    ///
378    /// let mut matrix = MatPolynomialRingZq::new(4, 3, ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap());
379    /// matrix.reverse_rows();
380    /// ```
381    pub fn reverse_rows(&mut self) {
382        self.matrix.reverse_rows()
383    }
384
385    /// Changes the modulus of the given matrix to the new modulus.
386    /// It takes the representation of each entry with coefficients in [0, q) as the new
387    /// matrix entries and reduces them by the new [`ModulusPolynomialRingZq`].
388    ///
389    /// Parameters:
390    /// - `modulus`: the new modulus of the matrix
391    ///
392    /// # Examples
393    /// ```
394    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
395    /// use std::str::FromStr;
396    /// let modulus0 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
397    /// let modulus1 = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 19").unwrap();
398    ///
399    /// let mut matrix = MatPolynomialRingZq::new(4, 3, modulus0);
400    ///
401    /// matrix.change_modulus(modulus1);
402    /// ```
403    ///
404    /// # Panics ...
405    /// - if `modulus` is smaller than `2`, or
406    /// - if the modulus polynomial is of degree smaller than `1`.
407    /// - if the leading coefficient is not `1.`
408    pub fn change_modulus(&mut self, modulus: impl Into<ModulusPolynomialRingZq>) {
409        self.modulus = modulus.into();
410        self.reduce();
411    }
412
413    /// Changes the modulus `q` of the given matrix to the new modulus `q`.
414    /// It takes the representation of each entry with coefficients in `[0, q)` as the new
415    /// matrix entries and reduces them by the new [`Modulus`].
416    ///
417    /// Parameters:
418    /// - `q`: the new modulus of the matrix
419    ///
420    /// # Examples
421    /// ```
422    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
423    /// use std::str::FromStr;
424    ///
425    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
426    ///
427    /// let mut matrix = MatPolynomialRingZq::new(4, 3, modulus);
428    ///
429    /// matrix.change_q(19);
430    /// ```
431    ///
432    /// # Panics ...
433    /// - if `modulus` is smaller than `2`.
434    pub fn change_q(&mut self, q: impl Into<Modulus>) {
435        self.modulus.change_q(q);
436        self.reduce();
437    }
438}
439
440#[cfg(test)]
441mod test_setter {
442    use crate::{
443        integer::{MatPolyOverZ, PolyOverZ},
444        integer_mod_q::{
445            MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq,
446        },
447        traits::{MatrixGetEntry, MatrixSetEntry, MatrixSetSubmatrix},
448    };
449    use std::str::FromStr;
450
451    const LARGE_PRIME: u64 = u64::MAX - 58;
452
453    /// Ensure that setting entries works with standard numbers.
454    #[test]
455    fn standard_value() {
456        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
457        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
458        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
459        let value = PolyOverZ::default();
460
461        poly_ring_mat.set_entry(1, 1, value).unwrap();
462
463        let entry_z: PolyOverZ = poly_ring_mat.get_entry(1, 1).unwrap();
464        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(1, 1).unwrap();
465
466        assert_eq!("0", entry_z.to_string());
467        assert_eq!("0", entry_zq.poly.to_string());
468        assert_eq!("4  1 0 0 1 mod 17", entry_zq.modulus.to_string());
469    }
470
471    /// Ensure that when using a [`PolyOverZ`] the set entry is actually reduced by the
472    /// modulus.
473    #[test]
474    fn set_entry_reduced() {
475        let id_mat = MatPolyOverZ::identity(2, 2);
476        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
477        let poly_mod = ModulusPolynomialRingZq::from(modulus);
478
479        let mut poly_mat = MatPolynomialRingZq::from((id_mat, &poly_mod));
480
481        poly_mat
482            .set_entry(0, 0, PolyOverZ::from_str("5  -1 0 0 0 16").unwrap())
483            .unwrap();
484
485        let entry: PolyOverZ = poly_mat.get_entry(0, 0).unwrap();
486        assert!(entry.is_zero());
487    }
488
489    /// Ensure that setting entries works with large numbers.
490    #[test]
491    fn large_positive() {
492        let modulus =
493            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
494        let poly_mat =
495            MatPolyOverZ::from_str(&format!("[[2  1 1, 1  42],[0, 2  {} 2]]", i64::MAX)).unwrap();
496        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
497        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MAX)).unwrap();
498
499        poly_ring_mat.set_entry(0, 0, value).unwrap();
500
501        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 0).unwrap();
502        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 0).unwrap();
503
504        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_z.to_string());
505        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_zq.poly.to_string());
506        assert_eq!(
507            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
508            entry_zq.modulus.to_string()
509        );
510    }
511
512    /// Ensure that setting entries works with referenced large numbers.
513    #[test]
514    fn large_positive_ref() {
515        let modulus =
516            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
517        let poly_mat = MatPolyOverZ::from_str(&format!(
518            "[[2  1 1, 1  42, 0],[0, 2  {} 2, 1  1]]",
519            i64::MAX
520        ))
521        .unwrap();
522        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
523        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MAX)).unwrap();
524
525        poly_ring_mat.set_entry(0, 0, &value).unwrap();
526
527        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 0).unwrap();
528        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 0).unwrap();
529
530        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_z.to_string());
531        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_zq.poly.to_string());
532        assert_eq!(
533            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
534            entry_zq.modulus.to_string()
535        );
536    }
537
538    /// Ensure that setting entries works with large negative numbers.
539    #[test]
540    fn large_negative() {
541        let modulus =
542            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
543        let poly_mat = MatPolyOverZ::from_str(&format!(
544            "[[2  1 1, 1  42],[0, 2  {} 2],[1  2, 0]]",
545            i64::MAX
546        ))
547        .unwrap();
548        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
549        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MIN)).unwrap();
550
551        poly_ring_mat.set_entry(0, 1, value).unwrap();
552
553        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 1).unwrap();
554        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 1).unwrap();
555
556        assert_eq!(
557            format!("3  1 {} 1", LARGE_PRIME - i64::MIN as u64),
558            entry_z.to_string()
559        );
560        assert_eq!(
561            format!("3  1 {} 1", LARGE_PRIME - i64::MIN as u64),
562            entry_zq.poly.to_string()
563        );
564        assert_eq!(
565            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
566            entry_zq.modulus.to_string()
567        );
568    }
569
570    /// Ensure that a wrong number of rows yields an Error.
571    #[test]
572    fn error_wrong_row() {
573        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
574        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
575        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
576        let value = PolyOverZ::default();
577
578        assert!(poly_ring_mat.set_entry(2, 0, &value).is_err());
579        assert!(poly_ring_mat.set_entry(-3, 0, value).is_err());
580    }
581
582    /// Ensure that a wrong number of columns yields an Error.
583    #[test]
584    fn error_wrong_column() {
585        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
586        let poly_mat =
587            MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42, 0],[0, 2  1 2, 1  17]]").unwrap();
588        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
589        let value = PolyOverZ::default();
590
591        assert!(poly_ring_mat.set_entry(0, 3, &value).is_err());
592        assert!(poly_ring_mat.set_entry(0, -4, value).is_err());
593    }
594
595    /// Ensure that differing moduli result in an error.
596    #[test]
597    fn modulus_error() {
598        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
599        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 2 1 mod 17").unwrap();
600        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
601        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus_1));
602        let value = PolynomialRingZq::from((&PolyOverZ::default(), &modulus_2));
603
604        assert!(poly_ring_mat.set_entry(1, 1, value).is_err());
605    }
606
607    /// Ensures that setting columns works fine for small entries.
608    #[test]
609    fn column_small_entries() {
610        let mut mat_1 =
611            MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 1  5, 1  6]] / 2  1 1 mod 6").unwrap();
612        let mat_2 = MatPolynomialRingZq::from_str("[[0],[1  -1]] / 2  1 1 mod 6").unwrap();
613        let cmp =
614            MatPolynomialRingZq::from_str("[[0, 0, 0],[0, 1  -1, 1  6]] / 2  1 1 mod 6").unwrap();
615
616        mat_1.set_column(1, &mat_2, 0).unwrap();
617
618        assert_eq!(cmp, mat_1);
619    }
620
621    /// Ensures that setting columns works fine for large entries.
622    #[test]
623    fn column_large_entries() {
624        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
625            "[[1  {}, 1  1, 1  3, 1  4],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
626            i64::MIN,
627            i64::MAX,
628            u64::MAX-1,
629            u64::MAX
630        ))
631        .unwrap();
632        let mat_2 = MatPolynomialRingZq::from_str(&format!(
633            "[[1  1, 1  {}],[1  {}, 0],[1  7, 1  -1]] / 2  1 1 mod {}",
634            i64::MIN,
635            i64::MAX,
636            u64::MAX
637        ))
638        .unwrap();
639        let cmp = MatPolynomialRingZq::from_str(&format!(
640            "[[1  {}, 1  1, 1  3, 1  4],[0, 1  4, 1  {}, 1  5],[1  -1, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
641            i64::MIN,
642            u64::MAX-1,
643            u64::MAX
644        ))
645        .unwrap();
646
647        mat_1.set_column(0, &mat_2, 1).unwrap();
648
649        assert_eq!(cmp, mat_1);
650    }
651
652    /// Ensures that setting the column to itself does not change anything.
653    #[test]
654    fn column_swap_same_entry() {
655        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
656            "[[1  {}, 1  1, 1  3, 1  4],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
657            i64::MIN,
658            i64::MAX,
659            u64::MAX-1,
660            u64::MAX
661        ))
662        .unwrap();
663        let cmp = mat_1.clone();
664
665        mat_1.set_column(0, &cmp, 0).unwrap();
666        mat_1.set_column(1, &cmp, 1).unwrap();
667
668        assert_eq!(cmp, mat_1);
669    }
670
671    /// Ensures that `set_column` returns an error if one of the specified columns is out of bounds.
672    #[test]
673    fn column_out_of_bounds() {
674        let mut mat_1 =
675            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
676                .unwrap();
677        let mat_2 = mat_1.clone();
678
679        assert!(mat_1.set_column(-3, &mat_2, 0).is_err());
680        assert!(mat_1.set_column(2, &mat_2, 0).is_err());
681        assert!(mat_1.set_column(1, &mat_2, -3).is_err());
682        assert!(mat_1.set_column(1, &mat_2, 2).is_err());
683    }
684
685    /// Ensures that mismatching row dimensions result in an error.
686    #[test]
687    fn column_mismatching_columns() {
688        let mut mat_1 =
689            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
690                .unwrap();
691        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
692
693        assert!(mat_1.set_column(0, &mat_2, 0).is_err());
694        assert!(mat_1.set_column(1, &mat_2, 1).is_err());
695    }
696
697    /// Ensures that mismatching moduli result in an error.
698    #[test]
699    fn column_mismatching_moduli() {
700        let mut mat_1 =
701            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
702        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 7").unwrap();
703
704        assert!(mat_1.set_column(0, &mat_2, 0).is_err());
705        assert!(mat_1.set_column(1, &mat_2, 1).is_err());
706    }
707
708    /// Ensures that setting rows works fine for small entries.
709    #[test]
710    fn row_small_entries() {
711        let mut mat_1 =
712            MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 2  5 6, 0]] / 2  1 1 mod 6").unwrap();
713        let mat_2 = MatPolynomialRingZq::from_str("[[0, 1  -1, 1  2]] / 2  1 1 mod 6").unwrap();
714        let cmp = MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 1  -1, 1  2]] / 2  1 1 mod 6")
715            .unwrap();
716
717        let _ = mat_1.set_row(1, &mat_2, 0);
718
719        assert_eq!(cmp, mat_1);
720    }
721
722    /// Ensures that setting rows works fine for large entries.
723    #[test]
724    fn row_large_entries() {
725        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
726            "[[1  {}, 1  1, 1  3, 1  4],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
727            i64::MIN,
728            i64::MAX,
729            u64::MAX-1,
730            u64::MAX
731        ))
732        .unwrap();
733        let mat_2 = MatPolynomialRingZq::from_str(&format!(
734            "[[0, 0, 0, 0],[1  {}, 0, 1  {}, 0]] / 2  1 1 mod {}",
735            i64::MIN,
736            i64::MAX,
737            u64::MAX
738        ))
739        .unwrap();
740        let cmp = MatPolynomialRingZq::from_str(&format!(
741            "[[1  {}, 0, 1  {}, 0],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
742            i64::MIN,
743            i64::MAX,
744            i64::MAX,
745            u64::MAX-1,
746            u64::MAX
747        ))
748        .unwrap();
749
750        let _ = mat_1.set_row(0, &mat_2, 1);
751
752        assert_eq!(cmp, mat_1);
753    }
754
755    /// Ensures that setting the rows to itself does not change anything.
756    #[test]
757    fn row_swap_same_entry() {
758        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
759            "[[1  {}, 1  1, 1  3, 1  4],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
760            i64::MIN,
761            i64::MAX,
762            u64::MAX-1,
763            u64::MAX
764        ))
765        .unwrap();
766        let cmp = mat_1.clone();
767
768        let _ = mat_1.set_row(0, &cmp, 0);
769        let _ = mat_1.set_row(1, &cmp, 1);
770
771        assert_eq!(cmp, mat_1);
772    }
773
774    /// Ensures that `set_row` returns an error if one of the specified rows is out of bounds.
775    #[test]
776    fn row_out_of_bounds() {
777        let mut mat_1 =
778            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
779                .unwrap();
780        let mat_2 = mat_1.clone();
781
782        assert!(mat_1.set_row(-6, &mat_2, 0).is_err());
783        assert!(mat_1.set_row(5, &mat_2, 0).is_err());
784        assert!(mat_1.set_row(2, &mat_2, -6).is_err());
785        assert!(mat_1.set_row(2, &mat_2, 5).is_err());
786    }
787
788    /// Ensures that mismatching column dimensions result in an error.
789    #[test]
790    fn row_mismatching_columns() {
791        let mut mat_1 =
792            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
793        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0, 0],[0, 0, 0],[0, 0, 0]] / 2  1 1 mod 6")
794            .unwrap();
795
796        assert!(mat_1.set_row(0, &mat_2, 0).is_err());
797        assert!(mat_1.set_row(1, &mat_2, 1).is_err());
798    }
799
800    /// Ensures that mismatching moduli result in an error.
801    #[test]
802    fn row_mismatching_moduli() {
803        let mut mat_1 =
804            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
805        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 7").unwrap();
806
807        assert!(mat_1.set_row(0, &mat_2, 0).is_err());
808        assert!(mat_1.set_row(1, &mat_2, 1).is_err());
809    }
810}
811
812#[cfg(test)]
813mod test_swaps {
814    use super::MatPolynomialRingZq;
815    use crate::{
816        integer_mod_q::ModulusPolynomialRingZq,
817        traits::{MatrixGetSubmatrix, MatrixSwaps},
818    };
819    use std::str::FromStr;
820
821    // Since swapping functions only call the existing tested functions for [`MatPolyOverZ`](crate::integer::MatPolyOverZ),
822    // we omit some tests that are already covered.
823
824    /// Ensures that swapping entries works
825    #[test]
826    fn entries_swapping() {
827        let mut matrix = MatPolynomialRingZq::from_str(
828            "[[1  1, 1  2, 1  3],[1  4, 2  5 6, 0]] / 3  1 2 1 mod 17",
829        )
830        .unwrap();
831        let cmp = MatPolynomialRingZq::from_str(
832            "[[1  1, 2  5 6, 1  3],[1  4, 1  2, 0]] / 3  1 2 1 mod 17",
833        )
834        .unwrap();
835
836        let _ = matrix.swap_entries(1, 1, 0, 1);
837
838        assert_eq!(cmp, matrix);
839    }
840
841    /// Ensures that `swap_entries` returns an error if one of the specified entries is out of bounds
842    #[test]
843    fn entries_out_of_bounds() {
844        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
845        let mut matrix = MatPolynomialRingZq::new(5, 2, modulus);
846
847        assert!(matrix.swap_entries(-6, 0, 0, 0).is_err());
848        assert!(matrix.swap_entries(0, -3, 0, 0).is_err());
849        assert!(matrix.swap_entries(0, 0, 5, 0).is_err());
850        assert!(matrix.swap_entries(0, 5, 0, 0).is_err());
851    }
852
853    /// Ensure that `swap_entries` can properly handle negative indexing.
854    #[test]
855    fn entries_negative_indexing() {
856        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap();
857        let mut matrix = MatPolynomialRingZq::identity(2, 2, modulus);
858
859        matrix.swap_entries(-2, -2, -2, -1).unwrap();
860        assert_eq!(
861            "[[0, 1  1],[0, 1  1]] / 3  1 0 1 mod 17",
862            matrix.to_string()
863        );
864    }
865
866    /// Ensures that swapping columns works fine for small entries
867    #[test]
868    fn columns_swapping() {
869        let mut matrix = MatPolynomialRingZq::from_str(
870            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 3  1 2 1 mod 17",
871        )
872        .unwrap();
873        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1],[1  4]] / 3  1 2 1 mod 17").unwrap();
874        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  3],[1  6]] / 3  1 2 1 mod 17").unwrap();
875        let cmp_vec_2 = MatPolynomialRingZq::from_str("[[1  2],[1  5]] / 3  1 2 1 mod 17").unwrap();
876
877        let _ = matrix.swap_columns(1, 2);
878
879        assert_eq!(cmp_vec_0, matrix.get_column(0).unwrap());
880        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
881        assert_eq!(cmp_vec_2, matrix.get_column(2).unwrap());
882    }
883
884    /// Ensures that `swap_columns` returns an error if one of the specified columns is out of bounds
885    #[test]
886    fn column_out_of_bounds() {
887        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
888        let mut matrix = MatPolynomialRingZq::new(5, 2, modulus);
889
890        assert!(matrix.swap_columns(-6, 0).is_err());
891        assert!(matrix.swap_columns(0, -6).is_err());
892        assert!(matrix.swap_columns(5, 0).is_err());
893        assert!(matrix.swap_columns(0, 5).is_err());
894    }
895
896    /// Ensures that swapping rows works
897    #[test]
898    fn rows_swapping() {
899        let mut matrix =
900            MatPolynomialRingZq::from_str("[[1  1, 1  2],[1  3, 2  4 5]] / 3  1 2 1 mod 17")
901                .unwrap();
902        let cmp_vec_0 =
903            MatPolynomialRingZq::from_str("[[1  3, 2  4 5]] / 3  1 2 1 mod 17").unwrap();
904        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  1, 1  2]] / 3  1 2 1 mod 17").unwrap();
905
906        let _ = matrix.swap_rows(1, 0);
907
908        assert_eq!(cmp_vec_0, matrix.get_row(0).unwrap());
909        assert_eq!(cmp_vec_1, matrix.get_row(1).unwrap());
910    }
911
912    /// Ensures that `swap_rows` returns an error if one of the specified rows is out of bounds
913    #[test]
914    fn row_out_of_bounds() {
915        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
916        let mut matrix = MatPolynomialRingZq::new(2, 4, modulus);
917
918        assert!(matrix.swap_rows(-3, 0).is_err());
919        assert!(matrix.swap_rows(0, -3).is_err());
920        assert!(matrix.swap_rows(4, 0).is_err());
921        assert!(matrix.swap_rows(0, 4).is_err());
922    }
923}
924
925#[cfg(test)]
926mod test_reverses {
927    use super::MatPolynomialRingZq;
928    use crate::traits::MatrixGetSubmatrix;
929    use std::str::FromStr;
930
931    // Since reversing functions only call the existing tested functions for [`MatPolyOverZ`](crate::integer::MatPolyOverZ),
932    // we omit some tests that are already covered.
933
934    /// Ensures that reversing columns works fine for small entries
935    #[test]
936    fn columns_reversing() {
937        let mut matrix = MatPolynomialRingZq::from_str(
938            "[[1  1, 1  2, 2  3 4],[0, 1  5, 1  6]] / 3  1 2 1 mod 17",
939        )
940        .unwrap();
941        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1],[0]] / 3  1 2 1 mod 17").unwrap();
942        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  2],[1  5]] / 3  1 2 1 mod 17").unwrap();
943        let cmp_vec_2 =
944            MatPolynomialRingZq::from_str("[[2  3 4],[1  6]] / 3  1 2 1 mod 17").unwrap();
945
946        matrix.reverse_columns();
947
948        assert_eq!(cmp_vec_2, matrix.get_column(0).unwrap());
949        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
950        assert_eq!(cmp_vec_0, matrix.get_column(2).unwrap());
951    }
952
953    /// Ensures that reversing rows works fine for small entries
954    #[test]
955    fn rows_reversing() {
956        let mut matrix =
957            MatPolynomialRingZq::from_str("[[1  1, 1  2],[2  3 4, 0]] / 3  1 2 1 mod 17").unwrap();
958        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1, 1  2]] / 3  1 2 1 mod 17").unwrap();
959        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[2  3 4, 0]] / 3  1 2 1 mod 17").unwrap();
960
961        matrix.reverse_rows();
962
963        assert_eq!(cmp_vec_1, matrix.get_row(0).unwrap());
964        assert_eq!(cmp_vec_0, matrix.get_row(1).unwrap());
965    }
966}
967
968#[cfg(test)]
969mod test_set_submatrix {
970    use crate::{
971        integer::MatPolyOverZ,
972        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
973        traits::MatrixSetSubmatrix,
974    };
975    use std::str::FromStr;
976
977    /// Ensure that the entire matrix can be set.
978    #[test]
979    fn entire_matrix() {
980        let modulus =
981            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
982        let mut mat = MatPolynomialRingZq::from((
983            &MatPolyOverZ::sample_uniform(10, 10, 5, -100, 100).unwrap(),
984            &modulus,
985        ));
986        let identity = MatPolynomialRingZq::identity(10, 10, &modulus);
987
988        mat.set_submatrix(0, 0, &identity, 0, 0, 9, 9).unwrap();
989
990        assert_eq!(identity, mat);
991    }
992
993    /// Ensure that matrix access out of bounds leadss to an error.
994    #[test]
995    fn out_of_bounds() {
996        let modulus =
997            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
998        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
999
1000        assert!(mat.set_submatrix(10, 0, &mat.clone(), 0, 0, 9, 9).is_err());
1001        assert!(mat.set_submatrix(0, 10, &mat.clone(), 0, 0, 9, 9).is_err());
1002        assert!(mat.set_submatrix(0, 0, &mat.clone(), 10, 0, 9, 9).is_err());
1003        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 10, 9, 9).is_err());
1004        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 10, 9).is_err());
1005        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, 10).is_err());
1006        assert!(mat.set_submatrix(-11, 0, &mat.clone(), 0, 0, 9, 9).is_err());
1007        assert!(mat.set_submatrix(0, -11, &mat.clone(), 0, 0, 9, 9).is_err());
1008        assert!(mat.set_submatrix(0, 0, &mat.clone(), -11, 0, 9, 9).is_err());
1009        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, -11, 9, 9).is_err());
1010        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, -11, 9).is_err());
1011        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, -11).is_err());
1012    }
1013
1014    /// Ensure that the function returns an error if the defined submatrix is too large
1015    /// and there is not enough space in the original matrix.
1016    #[test]
1017    fn submatrix_too_large() {
1018        let modulus =
1019            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1020        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
1021
1022        assert!(
1023            mat.set_submatrix(
1024                0,
1025                0,
1026                &MatPolynomialRingZq::identity(11, 11, &modulus),
1027                0,
1028                0,
1029                10,
1030                10
1031            )
1032            .is_err()
1033        );
1034        assert!(mat.set_submatrix(1, 2, &mat.clone(), 0, 0, 9, 9).is_err());
1035    }
1036
1037    /// Ensure that setting submatrices with large values works.
1038    #[test]
1039    fn large_values() {
1040        let modulus =
1041            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1042        let mut mat = MatPolynomialRingZq::from((
1043            &MatPolyOverZ::from_str(&format!(
1044                "[[1  1, 2  1 {}],[1  -{}, 0]]",
1045                u64::MAX,
1046                u64::MAX
1047            ))
1048            .unwrap(),
1049            &modulus,
1050        ));
1051        let cmp_mat = MatPolynomialRingZq::from((
1052            &MatPolyOverZ::from_str(&format!("[[1  -{}, 0],[1  -{}, 0]]", u64::MAX, u64::MAX))
1053                .unwrap(),
1054            &modulus,
1055        ));
1056
1057        mat.set_submatrix(0, 0, &mat.clone(), 1, 0, 1, 1).unwrap();
1058        assert_eq!(cmp_mat, mat);
1059    }
1060
1061    /// Ensure that setting with an undefined submatrix.
1062    #[test]
1063    #[should_panic]
1064    fn submatrix_negative() {
1065        let modulus =
1066            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1067        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
1068
1069        let _ = mat.set_submatrix(0, 0, &mat.clone(), 0, 9, 9, 5);
1070    }
1071
1072    /// Ensure that different moduli cause the set_submatrix to fail.
1073    #[test]
1074    fn different_moduli() {
1075        let modulus1 =
1076            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1077        let mut mat1 = MatPolynomialRingZq::identity(10, 10, &modulus1);
1078
1079        let modulus2 =
1080            ModulusPolynomialRingZq::from_str(&format!("4  2 0 0 1 mod {}", u64::MAX - 1)).unwrap();
1081        let mat2 = MatPolynomialRingZq::identity(10, 10, &modulus2);
1082
1083        assert!(mat1.set_submatrix(0, 0, &mat2.clone(), 0, 9, 0, 9).is_err());
1084    }
1085}
1086
1087#[cfg(test)]
1088mod test_change_modulus {
1089    use super::MatPolynomialRingZq;
1090    use crate::integer_mod_q::ModulusPolynomialRingZq;
1091    use std::str::FromStr;
1092
1093    /// Ensures that the modulus is changed correctly.
1094    #[test]
1095    fn modulus_correct() {
1096        let mut matrix = MatPolynomialRingZq::from_str(
1097            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod 7",
1098        )
1099        .unwrap();
1100        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 8").unwrap();
1101
1102        matrix.change_modulus(&modulus);
1103
1104        assert_eq!(
1105            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod 8",
1106            matrix.to_string()
1107        );
1108    }
1109
1110    /// Ensures that the modulus is changed correctly, if the modulus is big.
1111    #[test]
1112    fn big_modulus_correct() {
1113        let mut matrix = MatPolynomialRingZq::from_str(&format!(
1114            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod {}",
1115            i64::MAX
1116        ))
1117        .unwrap();
1118        let modulus =
1119            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1120
1121        matrix.change_modulus(&modulus);
1122
1123        assert_eq!(
1124            format!(
1125                "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod {}",
1126                u64::MAX
1127            ),
1128            matrix.to_string()
1129        );
1130    }
1131
1132    /// Ensures that the matrix is reduced correctly.
1133    #[test]
1134    fn reduced_correct() {
1135        let mut matrix = MatPolynomialRingZq::from_str(
1136            "[[1  1, 1  2, 1  3],[1  4, 1  5, 4  1 0 0 1]] / 8  1 0 0 0 0 0 0 1 mod 7",
1137        )
1138        .unwrap();
1139        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 2").unwrap();
1140
1141        matrix.change_modulus(&modulus);
1142
1143        assert_eq!(
1144            "[[1  1, 0, 1  1],[0, 1  1, 0]] / 4  1 0 0 1 mod 2",
1145            matrix.to_string()
1146        );
1147    }
1148}
1149
1150#[cfg(test)]
1151mod test_change_q {
1152    use super::MatPolynomialRingZq;
1153    use std::str::FromStr;
1154
1155    /// Ensures that the modulus `q` is changed correctly.
1156    #[test]
1157    fn q_correct() {
1158        let mut matrix = MatPolynomialRingZq::from_str(
1159            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod 7",
1160        )
1161        .unwrap();
1162
1163        matrix.change_q(8);
1164
1165        assert_eq!(
1166            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod 8",
1167            matrix.to_string()
1168        );
1169    }
1170
1171    /// Ensures that the modulus `q` is changed correctly, if the modulus is big.
1172    #[test]
1173    fn big_q_correct() {
1174        let mut matrix = MatPolynomialRingZq::from_str(&format!(
1175            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod {}",
1176            i64::MAX
1177        ))
1178        .unwrap();
1179
1180        matrix.change_q(u64::MAX);
1181
1182        assert_eq!(
1183            format!(
1184                "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 4  1 0 0 1 mod {}",
1185                u64::MAX
1186            ),
1187            matrix.to_string()
1188        );
1189    }
1190
1191    /// Ensures that the matrix is reduced correctly.
1192    #[test]
1193    fn reduced_correct() {
1194        let mut matrix = MatPolynomialRingZq::from_str(
1195            "[[1  1, 1  2, 1  3],[1  4, 1  5, 4  1 0 0 1]] / 4  1 0 0 1 mod 7",
1196        )
1197        .unwrap();
1198
1199        matrix.change_q(2);
1200
1201        assert_eq!(
1202            "[[1  1, 0, 1  1],[0, 1  1, 0]] / 4  1 0 0 1 mod 2",
1203            matrix.to_string()
1204        );
1205    }
1206}