Skip to main content

qfall_math/integer/mat_z/
set.rs

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