Skip to main content

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