qfall_math/integer/mat_z/
default.rs

1// Copyright © 2023 Marcel Luca Schmidt
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//! Initialize a [`MatZ`] with common defaults, e.g., zero and identity.
10
11use super::MatZ;
12use crate::utils::index::evaluate_indices;
13use flint_sys::fmpz_mat::{fmpz_mat_init, fmpz_mat_one};
14use std::{fmt::Display, mem::MaybeUninit};
15
16impl MatZ {
17    /// Creates a new matrix with `num_rows` rows, `num_cols` columns and
18    /// zeros as entries.
19    ///
20    /// Parameters:
21    /// - `num_rows`: number of rows the new matrix should have
22    /// - `num_cols`: number of columns the new matrix should have
23    ///
24    /// Returns a new [`MatZ`] instance of the provided dimensions.
25    ///
26    /// # Examples
27    /// ```
28    /// use qfall_math::integer::MatZ;
29    ///
30    /// let matrix = MatZ::new(5, 10);
31    /// ```
32    ///
33    /// # Panics ...
34    /// - if the number of rows or columns is negative, `0`, or does not fit into an [`i64`].
35    pub fn new(
36        num_rows: impl TryInto<i64> + Display,
37        num_cols: impl TryInto<i64> + Display,
38    ) -> Self {
39        let (num_rows_i64, num_cols_i64) = evaluate_indices(num_rows, num_cols).unwrap();
40
41        assert!(
42            num_rows_i64 != 0 && num_cols_i64 != 0,
43            "A matrix can not contain 0 rows or 0 columns"
44        );
45
46        let mut matrix = MaybeUninit::uninit();
47        unsafe {
48            fmpz_mat_init(matrix.as_mut_ptr(), num_rows_i64, num_cols_i64);
49
50            // Construct MatZ from previously initialized fmpz_mat
51            MatZ {
52                matrix: matrix.assume_init(),
53            }
54        }
55    }
56
57    /// Generate a `num_rows` times `num_columns` matrix with `1` on the
58    /// diagonal and `0` anywhere else.
59    ///
60    /// Parameters:
61    /// - `rum_rows`: the number of rows of the identity matrix
62    /// - `num_columns`: the number of columns of the identity matrix
63    ///
64    /// Returns a matrix with `1` across the diagonal and `0` anywhere else.
65    ///
66    /// # Examples
67    /// ```
68    /// use qfall_math::integer::MatZ;
69    ///
70    /// let matrix = MatZ::identity(2, 3);
71    ///
72    /// let identity = MatZ::identity(10, 10);
73    /// ```
74    ///
75    /// # Panics ...
76    /// - if the provided number of rows and columns are not suited to create a matrix.
77    ///   For further information see [`MatZ::new`].
78    pub fn identity(
79        num_rows: impl TryInto<i64> + Display,
80        num_cols: impl TryInto<i64> + Display,
81    ) -> Self {
82        let mut out = MatZ::new(num_rows, num_cols);
83        unsafe { fmpz_mat_one(&mut out.matrix) };
84        out
85    }
86}
87
88#[cfg(test)]
89mod test_new {
90    use crate::{
91        integer::{MatZ, Z},
92        traits::MatrixGetEntry,
93    };
94
95    /// Ensure that entries of a new matrix are `0`.
96    #[test]
97    fn entry_zero() {
98        let matrix = MatZ::new(2, 2);
99
100        let entry_1 = matrix.get_entry(0, 0).unwrap();
101        let entry_2 = matrix.get_entry(0, 1).unwrap();
102        let entry_3 = matrix.get_entry(1, 0).unwrap();
103        let entry_4 = matrix.get_entry(1, 1).unwrap();
104
105        assert_eq!(Z::ZERO, entry_1);
106        assert_eq!(Z::ZERO, entry_2);
107        assert_eq!(Z::ZERO, entry_3);
108        assert_eq!(Z::ZERO, entry_4);
109    }
110
111    /// Ensure that a new zero matrix fails with `0` as `num_cols`.
112    #[should_panic]
113    #[test]
114    fn error_zero_num_cols() {
115        let _ = MatZ::new(1, 0);
116    }
117
118    /// Ensure that a new zero matrix fails with `0` as `num_rows`.
119    #[should_panic]
120    #[test]
121    fn error_zero_num_rows() {
122        let _ = MatZ::new(0, 1);
123    }
124}
125
126#[cfg(test)]
127mod test_identity {
128    use crate::{
129        integer::{MatZ, Z},
130        traits::MatrixGetEntry,
131    };
132
133    /// Tests if an identity matrix is set from a zero matrix.
134    #[test]
135    fn identity() {
136        let matrix = MatZ::identity(10, 10);
137
138        for i in 0..10 {
139            for j in 0..10 {
140                if i != j {
141                    assert_eq!(Z::ZERO, matrix.get_entry(i, j).unwrap());
142                } else {
143                    assert_eq!(Z::ONE, matrix.get_entry(i, j).unwrap());
144                }
145            }
146        }
147    }
148
149    /// Tests if function works for a non-square matrix
150    #[test]
151    fn non_square_works() {
152        let matrix = MatZ::identity(10, 7);
153
154        for i in 0..10 {
155            for j in 0..7 {
156                if i != j {
157                    assert_eq!(Z::ZERO, matrix.get_entry(i, j).unwrap());
158                } else {
159                    assert_eq!(Z::ONE, matrix.get_entry(i, j).unwrap());
160                }
161            }
162        }
163
164        let matrix = MatZ::identity(7, 10);
165
166        for i in 0..7 {
167            for j in 0..10 {
168                if i != j {
169                    assert_eq!(Z::ZERO, matrix.get_entry(i, j).unwrap());
170                } else {
171                    assert_eq!(Z::ONE, matrix.get_entry(i, j).unwrap());
172                }
173            }
174        }
175    }
176}