qfall_math/integer/mat_poly_over_z/
set.rs

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