qfall_math/integer/mat_poly_over_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 [`MatPolyOverZ`].
10
11use super::MatPolyOverZ;
12use crate::{
13    error::MathError,
14    traits::{Concatenate, MatrixDimensions},
15};
16use flint_sys::fmpz_poly_mat::{fmpz_poly_mat_concat_horizontal, fmpz_poly_mat_concat_vertical};
17
18impl Concatenate for &MatPolyOverZ {
19    type Output = MatPolyOverZ;
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
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::MatPolyOverZ;
33    ///
34    /// let mat_1 = MatPolyOverZ::new(13, 5);
35    /// let mat_2 = MatPolyOverZ::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 = MatPolyOverZ::new(
55            self.get_num_rows() + other.get_num_rows(),
56            self.get_num_columns(),
57        );
58        unsafe {
59            fmpz_poly_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::MatPolyOverZ;
76    ///
77    /// let mat_1 = MatPolyOverZ::new(17, 5);
78    /// let mat_2 = MatPolyOverZ::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 = MatPolyOverZ::new(
98            self.get_num_rows(),
99            self.get_num_columns() + other.get_num_columns(),
100        );
101        unsafe {
102            fmpz_poly_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::MatPolyOverZ,
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 = MatPolyOverZ::new(13, 5);
121        let mat_2 = MatPolyOverZ::new(17, 5);
122        let mat_3 = MatPolyOverZ::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 = MatPolyOverZ::new(13, 5);
136        let mat_2 = MatPolyOverZ::new(17, 5);
137        let mat_3 = MatPolyOverZ::new(17, 6);
138
139        let mat_hor = mat_2.concat_horizontal(&mat_3).unwrap();
140
141        assert_eq!(11, mat_hor.get_num_columns());
142        assert_eq!(17, mat_hor.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 = MatPolyOverZ::from_str(&format!(
150            "[[1  1, 1  2, 1  {}],[0, 1  5, 1  {}]]",
151            i64::MIN,
152            u64::MAX
153        ))
154        .unwrap();
155        let mat_2 = MatPolyOverZ::from_str("[[1  -1, 1  2, 1  -17]]").unwrap();
156
157        let mat_vertical = mat_1.concat_vertical(&mat_2).unwrap();
158
159        let cmp_mat = MatPolyOverZ::from_str(&format!(
160            "[[1  1, 1  2, 1  {}],[0, 1  5, 1  {}],[1  -1, 1  2, 1  -17]]",
161            i64::MIN,
162            u64::MAX
163        ))
164        .unwrap();
165        assert_eq!(cmp_mat, mat_vertical);
166    }
167
168    /// Ensure that horizontal concatenation works correctly
169    #[test]
170    fn horizontally_correct() {
171        let mat_1 = MatPolyOverZ::from_str(&format!(
172            "[[1  1, 1  2, 1  {}],[0, 1  5, 1  {}]]",
173            i64::MIN,
174            u64::MAX
175        ))
176        .unwrap();
177        let mat_2 = MatPolyOverZ::from_str("[[1  -1, 1  2],[0, 1  5]]").unwrap();
178
179        let mat_horizontal = mat_1.concat_horizontal(&mat_2).unwrap();
180
181        let cmp_mat = MatPolyOverZ::from_str(&format!(
182            "[[1  1, 1  2, 1  {}, 1  -1, 1  2],[0, 1  5, 1  {}, 0, 1  5]]",
183            i64::MIN,
184            u64::MAX
185        ))
186        .unwrap();
187        assert_eq!(cmp_mat, mat_horizontal);
188    }
189}