qfall_math/integer/mat_z/
concat.rs

1// Copyright © 2023 Marvin Beckmann
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 concatenate two [`MatZ`].
10
11use super::MatZ;
12use crate::{
13    error::MathError,
14    traits::{Concatenate, MatrixDimensions},
15};
16use flint_sys::fmpz_mat::{fmpz_mat_concat_horizontal, fmpz_mat_concat_vertical};
17
18impl Concatenate for &MatZ {
19    type Output = MatZ;
20
21    /// Concatenates `self` with `other` vertically, i.e. `other` is added below.
22    ///
23    /// Parameters:
24    /// - `other`: the other matrix to concatenate with `self`
25    ///
26    /// Returns a vertical concatenation of the two matrices or a
27    /// an error, if the matrices can not be concatenated vertically.
28    ///
29    /// # Examples
30    /// ```
31    /// use qfall_math::traits::*;
32    /// use qfall_math::integer::MatZ;
33    ///
34    /// let mat_1 = MatZ::new(13, 5);
35    /// let mat_2 = MatZ::new(17, 5);
36    ///
37    /// let mat_vert = mat_1.concat_vertical(&mat_2).unwrap();
38    /// ```
39    ///
40    /// # Errors and Failures
41    /// - Returns a [`MathError`] of type
42    ///   [`MismatchingMatrixDimension`](MathError::MismatchingMatrixDimension)
43    ///   if the matrices can not be concatenated due to mismatching dimensions
44    fn concat_vertical(self, other: Self) -> Result<Self::Output, crate::error::MathError> {
45        if self.get_num_columns() != other.get_num_columns() {
46            return Err(MathError::MismatchingMatrixDimension(format!(
47                "Tried to concatenate vertically a '{}x{}' matrix and a '{}x{}' matrix.",
48                self.get_num_rows(),
49                self.get_num_columns(),
50                other.get_num_rows(),
51                other.get_num_columns()
52            )));
53        }
54        let mut out = MatZ::new(
55            self.get_num_rows() + other.get_num_rows(),
56            self.get_num_columns(),
57        );
58        unsafe {
59            fmpz_mat_concat_vertical(&mut out.matrix, &self.matrix, &other.matrix);
60        }
61        Ok(out)
62    }
63
64    /// Concatenates `self` with `other` horizontally, i.e. `other` is added on the right.
65    ///
66    /// Parameters:
67    /// - `other`: the other matrix to concatenate with `self`
68    ///
69    /// Returns a horizontal concatenation of the two matrices or a
70    /// an error, if the matrices can not be concatenated horizontally.
71    ///
72    /// # Examples
73    /// ```
74    /// use qfall_math::traits::*;
75    /// use qfall_math::integer::MatZ;
76    ///
77    /// let mat_1 = MatZ::new(17, 5);
78    /// let mat_2 = MatZ::new(17, 6);
79    ///
80    /// let mat_vert = mat_1.concat_horizontal(&mat_2).unwrap();
81    /// ```
82    ///
83    /// # Errors and Failures
84    /// - Returns a [`MathError`] of type
85    ///   [`MismatchingMatrixDimension`](MathError::MismatchingMatrixDimension)
86    ///   if the matrices can not be concatenated due to mismatching dimensions
87    fn concat_horizontal(self, other: Self) -> Result<Self::Output, crate::error::MathError> {
88        if self.get_num_rows() != other.get_num_rows() {
89            return Err(MathError::MismatchingMatrixDimension(format!(
90                "Tried to concatenate horizontally a '{}x{}' matrix and a '{}x{}' matrix.",
91                self.get_num_rows(),
92                self.get_num_columns(),
93                other.get_num_rows(),
94                other.get_num_columns()
95            )));
96        }
97        let mut out = MatZ::new(
98            self.get_num_rows(),
99            self.get_num_columns() + other.get_num_columns(),
100        );
101        unsafe {
102            fmpz_mat_concat_horizontal(&mut out.matrix, &self.matrix, &other.matrix);
103        }
104        Ok(out)
105    }
106}
107
108#[cfg(test)]
109mod test_concatenate {
110    use crate::{
111        integer::MatZ,
112        traits::{Concatenate, MatrixDimensions},
113    };
114    use std::str::FromStr;
115
116    /// Ensure that the dimensions are taken over correctly and an error occurs
117    /// if the dimensions mismatch
118    #[test]
119    fn dimensions_vertical() {
120        let mat_1 = MatZ::new(13, 5);
121        let mat_2 = MatZ::new(17, 5);
122        let mat_3 = MatZ::new(17, 6);
123
124        let mat_vert = mat_1.concat_vertical(&mat_2).unwrap();
125
126        assert_eq!(5, mat_vert.get_num_columns());
127        assert_eq!(30, mat_vert.get_num_rows());
128        assert!(mat_1.concat_vertical(&mat_3).is_err());
129    }
130
131    /// Ensure that the dimensions are taken over correctly and an error occurs
132    /// if the dimensions mismatch
133    #[test]
134    fn dimensions_horizontal() {
135        let mat_1 = MatZ::new(13, 5);
136        let mat_2 = MatZ::new(17, 5);
137        let mat_3 = MatZ::new(17, 6);
138
139        let mat_vert = mat_2.concat_horizontal(&mat_3).unwrap();
140
141        assert_eq!(11, mat_vert.get_num_columns());
142        assert_eq!(17, mat_vert.get_num_rows());
143        assert!(mat_1.concat_horizontal(&mat_2).is_err());
144    }
145
146    /// Ensure that vertical concatenation works correctly
147    #[test]
148    fn vertically_correct() {
149        let mat_1 =
150            MatZ::from_str(&format!("[[1, 2, {}],[4, 5, {}]]", i64::MIN, u64::MAX)).unwrap();
151        let mat_2 = MatZ::from_str("[[-1, 2, -17]]").unwrap();
152
153        let mat_vertical = mat_1.concat_vertical(&mat_2).unwrap();
154
155        let cmp_mat = MatZ::from_str(&format!(
156            "[[1, 2, {}],[4, 5, {}],[-1, 2, -17]]",
157            i64::MIN,
158            u64::MAX
159        ))
160        .unwrap();
161        assert_eq!(cmp_mat, mat_vertical);
162    }
163
164    /// Ensure that horizontal concatenation works correctly
165    #[test]
166    fn horizontally_correct() {
167        let mat_1 =
168            MatZ::from_str(&format!("[[1, 2, {}],[4, 5, {}]]", i64::MIN, u64::MAX)).unwrap();
169        let mat_2 = MatZ::from_str("[[-1, 2],[4, 5]]").unwrap();
170
171        let mat_horizontal = mat_1.concat_horizontal(&mat_2).unwrap();
172
173        let cmp_mat = MatZ::from_str(&format!(
174            "[[1, 2, {}, -1, 2],[4, 5, {}, 4, 5]]",
175            i64::MIN,
176            u64::MAX
177        ))
178        .unwrap();
179        assert_eq!(cmp_mat, mat_horizontal);
180    }
181}