qfall_math/integer/mat_z/
set.rs

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