qfall_math/integer_mod_q/mat_zq/
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 [`MatZq`] with common defaults, e.g., zero and identity.
10
11use super::MatZq;
12use crate::{integer_mod_q::Modulus, utils::index::evaluate_indices};
13use flint_sys::fmpz_mod_mat::{fmpz_mod_mat_init, fmpz_mod_mat_one};
14use std::{fmt::Display, mem::MaybeUninit};
15
16impl MatZq {
17    /// Creates a new matrix with `num_rows` rows, `num_cols` columns,
18    /// zeros as entries and `modulus` as the modulus.
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    /// - `modulus`: the common modulus of the matrix entries
24    ///
25    /// Returns a new [`MatZq`] instance of the provided dimensions.
26    ///
27    /// # Examples
28    /// ```
29    /// use qfall_math::integer_mod_q::MatZq;
30    ///
31    /// let matrix = MatZq::new(5, 10, 7);
32    /// ```
33    ///
34    /// # Panics ...
35    /// - if the number of rows or columns is negative, `0`, or does not fit into an [`i64`].
36    /// - if `modulus` is smaller than `2`.
37    pub fn new(
38        num_rows: impl TryInto<i64> + Display,
39        num_cols: impl TryInto<i64> + Display,
40        modulus: impl Into<Modulus>,
41    ) -> Self {
42        let (num_rows_i64, num_cols_i64) = evaluate_indices(num_rows, num_cols).unwrap();
43
44        assert!(
45            num_rows_i64 != 0 && num_cols_i64 != 0,
46            "A matrix can not contain 0 rows or 0 columns."
47        );
48
49        let modulus: Modulus = modulus.into();
50
51        let mut matrix = MaybeUninit::uninit();
52        unsafe {
53            fmpz_mod_mat_init(
54                matrix.as_mut_ptr(),
55                num_rows_i64,
56                num_cols_i64,
57                &modulus.get_fmpz_mod_ctx_struct().n[0],
58            );
59
60            MatZq {
61                matrix: matrix.assume_init(),
62                // we can unwrap here since modulus > 1 was checked before
63                modulus,
64            }
65        }
66    }
67
68    /// Generate a `num_rows` times `num_columns` matrix with `1` on the
69    /// diagonal and `0` anywhere else with a given modulus.
70    ///
71    /// Parameters:
72    /// - `rum_rows`: the number of rows of the identity matrix
73    /// - `num_columns`: the number of columns of the identity matrix
74    /// - `modulus`: the modulus of the matrix
75    ///
76    /// Returns a matrix with `1` across the diagonal and `0` anywhere else.
77    ///
78    /// # Examples
79    /// ```
80    /// use qfall_math::integer_mod_q::MatZq;
81    ///
82    /// let matrix = MatZq::identity(2, 3, 3);
83    ///
84    /// let identity = MatZq::identity(10, 10, 3);
85    /// ```
86    ///
87    /// # Panics ...
88    /// - if the provided number of rows and columns or the modulus are not suited to create a matrix.
89    ///   For further information see [`MatZq::new`].
90    pub fn identity(
91        num_rows: impl TryInto<i64> + Display,
92        num_cols: impl TryInto<i64> + Display,
93        modulus: impl Into<Modulus>,
94    ) -> Self {
95        let mut out = MatZq::new(num_rows, num_cols, modulus);
96        unsafe { fmpz_mod_mat_one(&mut out.matrix) };
97        out
98    }
99}
100
101#[cfg(test)]
102mod test_identity {
103    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
104
105    /// Tests if an identity matrix is set from a zero matrix.
106    #[test]
107    fn identity() {
108        let matrix = MatZq::identity(10, 10, 3);
109
110        for i in 0..10 {
111            for j in 0..10 {
112                let entry: Z = matrix.get_entry(i, j).unwrap();
113                if i != j {
114                    assert_eq!(0, entry);
115                } else {
116                    assert_eq!(1, entry);
117                }
118            }
119        }
120    }
121
122    /// Tests if function works for a non-square matrix.
123    #[test]
124    fn non_square_works() {
125        let matrix = MatZq::identity(10, 7, 3);
126
127        for i in 0..10 {
128            for j in 0..7 {
129                let entry: Z = matrix.get_entry(i, j).unwrap();
130                if i != j {
131                    assert_eq!(0, entry);
132                } else {
133                    assert_eq!(1, entry);
134                }
135            }
136        }
137
138        let matrix = MatZq::identity(7, 10, 3);
139
140        for i in 0..7 {
141            for j in 0..10 {
142                let entry: Z = matrix.get_entry(i, j).unwrap();
143                if i != j {
144                    assert_eq!(0, entry);
145                } else {
146                    assert_eq!(1, entry);
147                }
148            }
149        }
150    }
151
152    /// Tests if an identity matrix can be created using a large modulus.
153    #[test]
154    fn modulus_large() {
155        let matrix = MatZq::identity(10, 10, i64::MAX);
156
157        for i in 0..10 {
158            for j in 0..10 {
159                let entry: Z = matrix.get_entry(i, j).unwrap();
160                if i != j {
161                    assert_eq!(0, entry);
162                } else {
163                    assert_eq!(1, entry);
164                }
165            }
166        }
167    }
168
169    /// Assert that a modulus of `1` is not allowed.
170    #[should_panic]
171    #[test]
172    fn modulus_one() {
173        let _ = MatZq::identity(10, 10, 1);
174    }
175}
176
177#[cfg(test)]
178mod test_new {
179    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
180
181    /// Ensure that initialization works.
182    #[test]
183    fn initialization() {
184        let _ = MatZq::new(2, 2, 3);
185    }
186
187    /// Ensure that entries of a new matrix are `0`.
188    #[test]
189    fn entry_zero() {
190        let matrix = MatZq::new(2, 2, 3);
191
192        let entry_1: Z = matrix.get_entry(0, 0).unwrap();
193        let entry_2: Z = matrix.get_entry(0, 1).unwrap();
194        let entry_3: Z = matrix.get_entry(1, 0).unwrap();
195        let entry_4: Z = matrix.get_entry(1, 1).unwrap();
196
197        assert_eq!(0, entry_1);
198        assert_eq!(0, entry_2);
199        assert_eq!(0, entry_3);
200        assert_eq!(0, entry_4);
201    }
202
203    /// Ensure that a new zero matrix fails with `0` as `num_cols`.
204    #[should_panic]
205    #[test]
206    fn error_zero_num_cols() {
207        let _ = MatZq::new(1, 0, 3);
208    }
209
210    /// Ensure that a new zero matrix fails with `0` as `num_rows`.
211    #[should_panic]
212    #[test]
213    fn error_zero_num_rows() {
214        let _ = MatZq::new(0, 1, 3);
215    }
216
217    /// Ensure that an invalid modulus yields an error.
218    #[should_panic]
219    #[test]
220    fn invalid_modulus_error() {
221        let _ = MatZq::new(2, 2, 1);
222    }
223
224    /// Ensure that a negative modulus yields an error.
225    #[should_panic]
226    #[test]
227    fn negative_modulus_error() {
228        let _ = MatZq::new(2, 2, -3);
229    }
230
231    /// Ensure that a negative modulus yields an error.
232    #[should_panic]
233    #[test]
234    fn one_modulus_error() {
235        let _ = MatZq::new(2, 2, 1);
236    }
237}