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::PolynomialRingZq;
13use crate::macros::for_others::implement_for_owned;
14use crate::traits::{MatrixSetSubmatrix, MatrixSwaps};
15use crate::{error::MathError, integer::PolyOverZ, traits::MatrixSetEntry};
16use flint_sys::fmpz_poly_mat::{
17    fmpz_poly_mat_set, fmpz_poly_mat_window_clear, fmpz_poly_mat_window_init,
18};
19use flint_sys::{fmpz_poly::fmpz_poly_set, fmpz_poly_mat::fmpz_poly_mat_entry};
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
386#[cfg(test)]
387mod test_setter {
388    use crate::{
389        integer::{MatPolyOverZ, PolyOverZ},
390        integer_mod_q::{
391            MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq,
392        },
393        traits::{MatrixGetEntry, MatrixSetEntry, MatrixSetSubmatrix},
394    };
395    use std::str::FromStr;
396
397    const LARGE_PRIME: u64 = u64::MAX - 58;
398
399    /// Ensure that setting entries works with standard numbers.
400    #[test]
401    fn standard_value() {
402        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
403        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
404        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
405        let value = PolyOverZ::default();
406
407        poly_ring_mat.set_entry(1, 1, value).unwrap();
408
409        let entry_z: PolyOverZ = poly_ring_mat.get_entry(1, 1).unwrap();
410        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(1, 1).unwrap();
411
412        assert_eq!("0", entry_z.to_string());
413        assert_eq!("0", entry_zq.poly.to_string());
414        assert_eq!("4  1 0 0 1 mod 17", entry_zq.modulus.to_string());
415    }
416
417    /// Ensure that when using a [`PolyOverZ`] the set entry is actually reduced by the
418    /// modulus.
419    #[test]
420    fn set_entry_reduced() {
421        let id_mat = MatPolyOverZ::identity(2, 2);
422        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
423
424        let mut poly_mat = MatPolynomialRingZq::from((id_mat, modulus));
425
426        poly_mat
427            .set_entry(0, 0, PolyOverZ::from_str("5  -1 0 0 0 16").unwrap())
428            .unwrap();
429
430        let entry: PolyOverZ = poly_mat.get_entry(0, 0).unwrap();
431        assert!(entry.is_zero());
432    }
433
434    /// Ensure that setting entries works with large numbers.
435    #[test]
436    fn large_positive() {
437        let modulus =
438            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
439        let poly_mat =
440            MatPolyOverZ::from_str(&format!("[[2  1 1, 1  42],[0, 2  {} 2]]", i64::MAX)).unwrap();
441        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
442        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MAX)).unwrap();
443
444        poly_ring_mat.set_entry(0, 0, value).unwrap();
445
446        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 0).unwrap();
447        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 0).unwrap();
448
449        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_z.to_string());
450        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_zq.poly.to_string());
451        assert_eq!(
452            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
453            entry_zq.modulus.to_string()
454        );
455    }
456
457    /// Ensure that setting entries works with referenced large numbers.
458    #[test]
459    fn large_positive_ref() {
460        let modulus =
461            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
462        let poly_mat = MatPolyOverZ::from_str(&format!(
463            "[[2  1 1, 1  42, 0],[0, 2  {} 2, 1  1]]",
464            i64::MAX
465        ))
466        .unwrap();
467        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
468        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MAX)).unwrap();
469
470        poly_ring_mat.set_entry(0, 0, &value).unwrap();
471
472        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 0).unwrap();
473        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 0).unwrap();
474
475        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_z.to_string());
476        assert_eq!(format!("3  1 {} 1", i64::MAX), entry_zq.poly.to_string());
477        assert_eq!(
478            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
479            entry_zq.modulus.to_string()
480        );
481    }
482
483    /// Ensure that setting entries works with large negative numbers.
484    #[test]
485    fn large_negative() {
486        let modulus =
487            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
488        let poly_mat = MatPolyOverZ::from_str(&format!(
489            "[[2  1 1, 1  42],[0, 2  {} 2],[1  2, 0]]",
490            i64::MAX
491        ))
492        .unwrap();
493        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
494        let value = PolyOverZ::from_str(&format!("3  1 {} 1", i64::MIN)).unwrap();
495
496        poly_ring_mat.set_entry(0, 1, value).unwrap();
497
498        let entry_z: PolyOverZ = poly_ring_mat.get_entry(0, 1).unwrap();
499        let entry_zq: PolynomialRingZq = poly_ring_mat.get_entry(0, 1).unwrap();
500
501        assert_eq!(
502            format!("3  1 {} 1", LARGE_PRIME - i64::MIN as u64),
503            entry_z.to_string()
504        );
505        assert_eq!(
506            format!("3  1 {} 1", LARGE_PRIME - i64::MIN as u64),
507            entry_zq.poly.to_string()
508        );
509        assert_eq!(
510            format!("4  1 0 0 1 mod {LARGE_PRIME}"),
511            entry_zq.modulus.to_string()
512        );
513    }
514
515    /// Ensure that a wrong number of rows yields an Error.
516    #[test]
517    fn error_wrong_row() {
518        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
519        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
520        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
521        let value = PolyOverZ::default();
522
523        assert!(poly_ring_mat.set_entry(2, 0, &value).is_err());
524        assert!(poly_ring_mat.set_entry(-3, 0, value).is_err());
525    }
526
527    /// Ensure that a wrong number of columns yields an Error.
528    #[test]
529    fn error_wrong_column() {
530        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
531        let poly_mat =
532            MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42, 0],[0, 2  1 2, 1  17]]").unwrap();
533        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
534        let value = PolyOverZ::default();
535
536        assert!(poly_ring_mat.set_entry(0, 3, &value).is_err());
537        assert!(poly_ring_mat.set_entry(0, -4, value).is_err());
538    }
539
540    /// Ensure that differing moduli result in an error.
541    #[test]
542    fn modulus_error() {
543        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
544        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
545        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
546        let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus_1));
547        let value = PolynomialRingZq::from((&PolyOverZ::default(), &modulus_2));
548
549        assert!(poly_ring_mat.set_entry(1, 1, value).is_err());
550    }
551
552    /// Ensures that setting columns works fine for small entries.
553    #[test]
554    fn column_small_entries() {
555        let mut mat_1 =
556            MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 1  5, 1  6]] / 2  1 1 mod 6").unwrap();
557        let mat_2 = MatPolynomialRingZq::from_str("[[0],[1  -1]] / 2  1 1 mod 6").unwrap();
558        let cmp =
559            MatPolynomialRingZq::from_str("[[0, 0, 0],[0, 1  -1, 1  6]] / 2  1 1 mod 6").unwrap();
560
561        mat_1.set_column(1, &mat_2, 0).unwrap();
562
563        assert_eq!(cmp, mat_1);
564    }
565
566    /// Ensures that setting columns works fine for large entries.
567    #[test]
568    fn column_large_entries() {
569        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
570            "[[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 {}",
571            i64::MIN,
572            i64::MAX,
573            u64::MAX-1,
574            u64::MAX
575        ))
576        .unwrap();
577        let mat_2 = MatPolynomialRingZq::from_str(&format!(
578            "[[1  1, 1  {}],[1  {}, 0],[1  7, 1  -1]] / 2  1 1 mod {}",
579            i64::MIN,
580            i64::MAX,
581            u64::MAX
582        ))
583        .unwrap();
584        let cmp = MatPolynomialRingZq::from_str(&format!(
585            "[[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 {}",
586            i64::MIN,
587            u64::MAX-1,
588            u64::MAX
589        ))
590        .unwrap();
591
592        mat_1.set_column(0, &mat_2, 1).unwrap();
593
594        assert_eq!(cmp, mat_1);
595    }
596
597    /// Ensures that setting the column to itself does not change anything.
598    #[test]
599    fn column_swap_same_entry() {
600        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
601            "[[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 {}",
602            i64::MIN,
603            i64::MAX,
604            u64::MAX-1,
605            u64::MAX
606        ))
607        .unwrap();
608        let cmp = mat_1.clone();
609
610        mat_1.set_column(0, &cmp, 0).unwrap();
611        mat_1.set_column(1, &cmp, 1).unwrap();
612
613        assert_eq!(cmp, mat_1);
614    }
615
616    /// Ensures that `set_column` returns an error if one of the specified columns is out of bounds.
617    #[test]
618    fn column_out_of_bounds() {
619        let mut mat_1 =
620            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
621                .unwrap();
622        let mat_2 = mat_1.clone();
623
624        assert!(mat_1.set_column(-3, &mat_2, 0).is_err());
625        assert!(mat_1.set_column(2, &mat_2, 0).is_err());
626        assert!(mat_1.set_column(1, &mat_2, -3).is_err());
627        assert!(mat_1.set_column(1, &mat_2, 2).is_err());
628    }
629
630    /// Ensures that mismatching row dimensions result in an error.
631    #[test]
632    fn column_mismatching_columns() {
633        let mut mat_1 =
634            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
635                .unwrap();
636        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
637
638        assert!(mat_1.set_column(0, &mat_2, 0).is_err());
639        assert!(mat_1.set_column(1, &mat_2, 1).is_err());
640    }
641
642    /// Ensures that mismatching moduli result in an error.
643    #[test]
644    fn column_mismatching_moduli() {
645        let mut mat_1 =
646            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
647        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 7").unwrap();
648
649        assert!(mat_1.set_column(0, &mat_2, 0).is_err());
650        assert!(mat_1.set_column(1, &mat_2, 1).is_err());
651    }
652
653    /// Ensures that setting rows works fine for small entries.
654    #[test]
655    fn row_small_entries() {
656        let mut mat_1 =
657            MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 2  5 6, 0]] / 2  1 1 mod 6").unwrap();
658        let mat_2 = MatPolynomialRingZq::from_str("[[0, 1  -1, 1  2]] / 2  1 1 mod 6").unwrap();
659        let cmp = MatPolynomialRingZq::from_str("[[0, 1  2, 0],[0, 1  -1, 1  2]] / 2  1 1 mod 6")
660            .unwrap();
661
662        let _ = mat_1.set_row(1, &mat_2, 0);
663
664        assert_eq!(cmp, mat_1);
665    }
666
667    /// Ensures that setting rows works fine for large entries.
668    #[test]
669    fn row_large_entries() {
670        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
671            "[[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 {}",
672            i64::MIN,
673            i64::MAX,
674            u64::MAX-1,
675            u64::MAX
676        ))
677        .unwrap();
678        let mat_2 = MatPolynomialRingZq::from_str(&format!(
679            "[[0, 0, 0, 0],[1  {}, 0, 1  {}, 0]] / 2  1 1 mod {}",
680            i64::MIN,
681            i64::MAX,
682            u64::MAX
683        ))
684        .unwrap();
685        let cmp = MatPolynomialRingZq::from_str(&format!(
686            "[[1  {}, 0, 1  {}, 0],[1  {}, 1  4, 1  {}, 1  5],[1  7, 1  6, 2  8 9, 0]] / 2  1 1 mod {}",
687            i64::MIN,
688            i64::MAX,
689            i64::MAX,
690            u64::MAX-1,
691            u64::MAX
692        ))
693        .unwrap();
694
695        let _ = mat_1.set_row(0, &mat_2, 1);
696
697        assert_eq!(cmp, mat_1);
698    }
699
700    /// Ensures that setting the rows to itself does not change anything.
701    #[test]
702    fn row_swap_same_entry() {
703        let mut mat_1 = MatPolynomialRingZq::from_str(&format!(
704            "[[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 {}",
705            i64::MIN,
706            i64::MAX,
707            u64::MAX-1,
708            u64::MAX
709        ))
710        .unwrap();
711        let cmp = mat_1.clone();
712
713        let _ = mat_1.set_row(0, &cmp, 0);
714        let _ = mat_1.set_row(1, &cmp, 1);
715
716        assert_eq!(cmp, mat_1);
717    }
718
719    /// Ensures that `set_row` returns an error if one of the specified rows is out of bounds.
720    #[test]
721    fn row_out_of_bounds() {
722        let mut mat_1 =
723            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6")
724                .unwrap();
725        let mat_2 = mat_1.clone();
726
727        assert!(mat_1.set_row(-6, &mat_2, 0).is_err());
728        assert!(mat_1.set_row(5, &mat_2, 0).is_err());
729        assert!(mat_1.set_row(2, &mat_2, -6).is_err());
730        assert!(mat_1.set_row(2, &mat_2, 5).is_err());
731    }
732
733    /// Ensures that mismatching column dimensions result in an error.
734    #[test]
735    fn row_mismatching_columns() {
736        let mut mat_1 =
737            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
738        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0, 0],[0, 0, 0],[0, 0, 0]] / 2  1 1 mod 6")
739            .unwrap();
740
741        assert!(mat_1.set_row(0, &mat_2, 0).is_err());
742        assert!(mat_1.set_row(1, &mat_2, 1).is_err());
743    }
744
745    /// Ensures that mismatching moduli result in an error.
746    #[test]
747    fn row_mismatching_moduli() {
748        let mut mat_1 =
749            MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 6").unwrap();
750        let mat_2 = MatPolynomialRingZq::from_str("[[0, 0],[0, 0],[0, 0]] / 2  1 1 mod 7").unwrap();
751
752        assert!(mat_1.set_row(0, &mat_2, 0).is_err());
753        assert!(mat_1.set_row(1, &mat_2, 1).is_err());
754    }
755}
756
757#[cfg(test)]
758mod test_swaps {
759    use super::MatPolynomialRingZq;
760    use crate::{
761        integer_mod_q::ModulusPolynomialRingZq,
762        traits::{MatrixGetSubmatrix, MatrixSwaps},
763    };
764    use std::str::FromStr;
765
766    // Since swapping functions only call the existing tested functions for [`MatPolyOverZ`](crate::integer::MatPolyOverZ),
767    // we omit some tests that are already covered.
768
769    /// Ensures that swapping entries works
770    #[test]
771    fn entries_swapping() {
772        let mut matrix = MatPolynomialRingZq::from_str(
773            "[[1  1, 1  2, 1  3],[1  4, 2  5 6, 0]] / 3  1 2 3 mod 17",
774        )
775        .unwrap();
776        let cmp = MatPolynomialRingZq::from_str(
777            "[[1  1, 2  5 6, 1  3],[1  4, 1  2, 0]] / 3  1 2 3 mod 17",
778        )
779        .unwrap();
780
781        let _ = matrix.swap_entries(1, 1, 0, 1);
782
783        assert_eq!(cmp, matrix);
784    }
785
786    /// Ensures that `swap_entries` returns an error if one of the specified entries is out of bounds
787    #[test]
788    fn entries_out_of_bounds() {
789        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
790        let mut matrix = MatPolynomialRingZq::new(5, 2, modulus);
791
792        assert!(matrix.swap_entries(-6, 0, 0, 0).is_err());
793        assert!(matrix.swap_entries(0, -3, 0, 0).is_err());
794        assert!(matrix.swap_entries(0, 0, 5, 0).is_err());
795        assert!(matrix.swap_entries(0, 5, 0, 0).is_err());
796    }
797
798    /// Ensure that `swap_entries` can properly handle negative indexing.
799    #[test]
800    fn entries_negative_indexing() {
801        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap();
802        let mut matrix = MatPolynomialRingZq::identity(2, 2, modulus);
803
804        matrix.swap_entries(-2, -2, -2, -1).unwrap();
805        assert_eq!(
806            "[[0, 1  1],[0, 1  1]] / 3  1 0 1 mod 17",
807            matrix.to_string()
808        );
809    }
810
811    /// Ensures that swapping columns works fine for small entries
812    #[test]
813    fn columns_swapping() {
814        let mut matrix = MatPolynomialRingZq::from_str(
815            "[[1  1, 1  2, 1  3],[1  4, 1  5, 1  6]] / 3  1 2 3 mod 17",
816        )
817        .unwrap();
818        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1],[1  4]] / 3  1 2 3 mod 17").unwrap();
819        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  3],[1  6]] / 3  1 2 3 mod 17").unwrap();
820        let cmp_vec_2 = MatPolynomialRingZq::from_str("[[1  2],[1  5]] / 3  1 2 3 mod 17").unwrap();
821
822        let _ = matrix.swap_columns(1, 2);
823
824        assert_eq!(cmp_vec_0, matrix.get_column(0).unwrap());
825        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
826        assert_eq!(cmp_vec_2, matrix.get_column(2).unwrap());
827    }
828
829    /// Ensures that `swap_columns` returns an error if one of the specified columns is out of bounds
830    #[test]
831    fn column_out_of_bounds() {
832        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
833        let mut matrix = MatPolynomialRingZq::new(5, 2, modulus);
834
835        assert!(matrix.swap_columns(-6, 0).is_err());
836        assert!(matrix.swap_columns(0, -6).is_err());
837        assert!(matrix.swap_columns(5, 0).is_err());
838        assert!(matrix.swap_columns(0, 5).is_err());
839    }
840
841    /// Ensures that swapping rows works
842    #[test]
843    fn rows_swapping() {
844        let mut matrix =
845            MatPolynomialRingZq::from_str("[[1  1, 1  2],[1  3, 2  4 5]] / 3  1 2 3 mod 17")
846                .unwrap();
847        let cmp_vec_0 =
848            MatPolynomialRingZq::from_str("[[1  3, 2  4 5]] / 3  1 2 3 mod 17").unwrap();
849        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  1, 1  2]] / 3  1 2 3 mod 17").unwrap();
850
851        let _ = matrix.swap_rows(1, 0);
852
853        assert_eq!(cmp_vec_0, matrix.get_row(0).unwrap());
854        assert_eq!(cmp_vec_1, matrix.get_row(1).unwrap());
855    }
856
857    /// Ensures that `swap_rows` returns an error if one of the specified rows is out of bounds
858    #[test]
859    fn row_out_of_bounds() {
860        let modulus = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
861        let mut matrix = MatPolynomialRingZq::new(2, 4, modulus);
862
863        assert!(matrix.swap_rows(-3, 0).is_err());
864        assert!(matrix.swap_rows(0, -3).is_err());
865        assert!(matrix.swap_rows(4, 0).is_err());
866        assert!(matrix.swap_rows(0, 4).is_err());
867    }
868}
869
870#[cfg(test)]
871mod test_reverses {
872    use super::MatPolynomialRingZq;
873    use crate::traits::MatrixGetSubmatrix;
874    use std::str::FromStr;
875
876    // Since reversing functions only call the existing tested functions for [`MatPolyOverZ`](crate::integer::MatPolyOverZ),
877    // we omit some tests that are already covered.
878
879    /// Ensures that reversing columns works fine for small entries
880    #[test]
881    fn columns_reversing() {
882        let mut matrix = MatPolynomialRingZq::from_str(
883            "[[1  1, 1  2, 2  3 4],[0, 1  5, 1  6]] / 3  1 2 3 mod 17",
884        )
885        .unwrap();
886        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1],[0]] / 3  1 2 3 mod 17").unwrap();
887        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[1  2],[1  5]] / 3  1 2 3 mod 17").unwrap();
888        let cmp_vec_2 =
889            MatPolynomialRingZq::from_str("[[2  3 4],[1  6]] / 3  1 2 3 mod 17").unwrap();
890
891        matrix.reverse_columns();
892
893        assert_eq!(cmp_vec_2, matrix.get_column(0).unwrap());
894        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
895        assert_eq!(cmp_vec_0, matrix.get_column(2).unwrap());
896    }
897
898    /// Ensures that reversing rows works fine for small entries
899    #[test]
900    fn rows_reversing() {
901        let mut matrix =
902            MatPolynomialRingZq::from_str("[[1  1, 1  2],[2  3 4, 0]] / 3  1 2 3 mod 17").unwrap();
903        let cmp_vec_0 = MatPolynomialRingZq::from_str("[[1  1, 1  2]] / 3  1 2 3 mod 17").unwrap();
904        let cmp_vec_1 = MatPolynomialRingZq::from_str("[[2  3 4, 0]] / 3  1 2 3 mod 17").unwrap();
905
906        matrix.reverse_rows();
907
908        assert_eq!(cmp_vec_1, matrix.get_row(0).unwrap());
909        assert_eq!(cmp_vec_0, matrix.get_row(1).unwrap());
910    }
911}
912
913#[cfg(test)]
914mod test_set_submatrix {
915    use crate::{
916        integer::MatPolyOverZ,
917        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
918        traits::MatrixSetSubmatrix,
919    };
920    use std::str::FromStr;
921
922    /// Ensure that the entire matrix can be set.
923    #[test]
924    fn entire_matrix() {
925        let modulus =
926            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
927        let mut mat = MatPolynomialRingZq::from((
928            &MatPolyOverZ::sample_uniform(10, 10, 5, -100, 100).unwrap(),
929            &modulus,
930        ));
931        let identity = MatPolynomialRingZq::identity(10, 10, &modulus);
932
933        mat.set_submatrix(0, 0, &identity, 0, 0, 9, 9).unwrap();
934
935        assert_eq!(identity, mat);
936    }
937
938    /// Ensure that matrix access out of bounds leadss to an error.
939    #[test]
940    fn out_of_bounds() {
941        let modulus =
942            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
943        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
944
945        assert!(mat.set_submatrix(10, 0, &mat.clone(), 0, 0, 9, 9).is_err());
946        assert!(mat.set_submatrix(0, 10, &mat.clone(), 0, 0, 9, 9).is_err());
947        assert!(mat.set_submatrix(0, 0, &mat.clone(), 10, 0, 9, 9).is_err());
948        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 10, 9, 9).is_err());
949        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 10, 9).is_err());
950        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, 10).is_err());
951        assert!(mat.set_submatrix(-11, 0, &mat.clone(), 0, 0, 9, 9).is_err());
952        assert!(mat.set_submatrix(0, -11, &mat.clone(), 0, 0, 9, 9).is_err());
953        assert!(mat.set_submatrix(0, 0, &mat.clone(), -11, 0, 9, 9).is_err());
954        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, -11, 9, 9).is_err());
955        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, -11, 9).is_err());
956        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, -11).is_err());
957    }
958
959    /// Ensure that the function returns an error if the defined submatrix is too large
960    /// and there is not enough space in the original matrix.
961    #[test]
962    fn submatrix_too_large() {
963        let modulus =
964            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
965        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
966
967        assert!(
968            mat.set_submatrix(
969                0,
970                0,
971                &MatPolynomialRingZq::identity(11, 11, &modulus),
972                0,
973                0,
974                10,
975                10
976            )
977            .is_err()
978        );
979        assert!(mat.set_submatrix(1, 2, &mat.clone(), 0, 0, 9, 9).is_err());
980    }
981
982    /// Ensure that setting submatrices with large values works.
983    #[test]
984    fn large_values() {
985        let modulus =
986            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
987        let mut mat = MatPolynomialRingZq::from((
988            &MatPolyOverZ::from_str(&format!(
989                "[[1  1, 2  1 {}],[1  -{}, 0]]",
990                u64::MAX,
991                u64::MAX
992            ))
993            .unwrap(),
994            &modulus,
995        ));
996        let cmp_mat = MatPolynomialRingZq::from((
997            &MatPolyOverZ::from_str(&format!("[[1  -{}, 0],[1  -{}, 0]]", u64::MAX, u64::MAX))
998                .unwrap(),
999            &modulus,
1000        ));
1001
1002        mat.set_submatrix(0, 0, &mat.clone(), 1, 0, 1, 1).unwrap();
1003        assert_eq!(cmp_mat, mat);
1004    }
1005
1006    /// Ensure that setting with an undefined submatrix.
1007    #[test]
1008    #[should_panic]
1009    fn submatrix_negative() {
1010        let modulus =
1011            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1012        let mut mat = MatPolynomialRingZq::identity(10, 10, &modulus);
1013
1014        let _ = mat.set_submatrix(0, 0, &mat.clone(), 0, 9, 9, 5);
1015    }
1016
1017    /// Ensure that different moduli cause the set_submatrix to fail.
1018    #[test]
1019    fn different_moduli() {
1020        let modulus1 =
1021            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
1022        let mut mat1 = MatPolynomialRingZq::identity(10, 10, &modulus1);
1023
1024        let modulus2 =
1025            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 2 mod {}", u64::MAX - 1)).unwrap();
1026        let mat2 = MatPolynomialRingZq::identity(10, 10, &modulus2);
1027
1028        assert!(mat1.set_submatrix(0, 0, &mat2.clone(), 0, 9, 0, 9).is_err());
1029    }
1030}