Skip to main content

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 flint3_sys::{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(),
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 modulus = modulus.into();
96        let mut out = MatZq::new(num_rows, num_cols, &modulus);
97        unsafe { fmpz_mod_mat_one(&mut out.matrix, modulus.get_fmpz_mod_ctx_struct()) };
98        out
99    }
100}
101
102#[cfg(test)]
103mod test_identity {
104    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
105
106    /// Tests if an identity matrix is set from a zero matrix.
107    #[test]
108    fn identity() {
109        let matrix = MatZq::identity(10, 10, 3);
110
111        for i in 0..10 {
112            for j in 0..10 {
113                let entry: Z = matrix.get_entry(i, j).unwrap();
114                if i != j {
115                    assert_eq!(0, entry);
116                } else {
117                    assert_eq!(1, entry);
118                }
119            }
120        }
121    }
122
123    /// Tests if function works for a non-square matrix.
124    #[test]
125    fn non_square_works() {
126        let matrix = MatZq::identity(10, 7, 3);
127
128        for i in 0..10 {
129            for j in 0..7 {
130                let entry: Z = matrix.get_entry(i, j).unwrap();
131                if i != j {
132                    assert_eq!(0, entry);
133                } else {
134                    assert_eq!(1, entry);
135                }
136            }
137        }
138
139        let matrix = MatZq::identity(7, 10, 3);
140
141        for i in 0..7 {
142            for j in 0..10 {
143                let entry: Z = matrix.get_entry(i, j).unwrap();
144                if i != j {
145                    assert_eq!(0, entry);
146                } else {
147                    assert_eq!(1, entry);
148                }
149            }
150        }
151    }
152
153    /// Tests if an identity matrix can be created using a large modulus.
154    #[test]
155    fn modulus_large() {
156        let matrix = MatZq::identity(10, 10, i64::MAX);
157
158        for i in 0..10 {
159            for j in 0..10 {
160                let entry: Z = matrix.get_entry(i, j).unwrap();
161                if i != j {
162                    assert_eq!(0, entry);
163                } else {
164                    assert_eq!(1, entry);
165                }
166            }
167        }
168    }
169
170    /// Assert that a modulus of `1` is not allowed.
171    #[should_panic]
172    #[test]
173    fn modulus_one() {
174        let _ = MatZq::identity(10, 10, 1);
175    }
176}
177
178#[cfg(test)]
179mod test_new {
180    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
181
182    /// Ensure that initialization works.
183    #[test]
184    fn initialization() {
185        let _ = MatZq::new(2, 2, 3);
186    }
187
188    /// Ensure that entries of a new matrix are `0`.
189    #[test]
190    fn entry_zero() {
191        let matrix = MatZq::new(2, 2, 3);
192
193        let entry_1: Z = matrix.get_entry(0, 0).unwrap();
194        let entry_2: Z = matrix.get_entry(0, 1).unwrap();
195        let entry_3: Z = matrix.get_entry(1, 0).unwrap();
196        let entry_4: Z = matrix.get_entry(1, 1).unwrap();
197
198        assert_eq!(0, entry_1);
199        assert_eq!(0, entry_2);
200        assert_eq!(0, entry_3);
201        assert_eq!(0, entry_4);
202    }
203
204    /// Ensure that a new zero matrix fails with `0` as `num_cols`.
205    #[should_panic]
206    #[test]
207    fn error_zero_num_cols() {
208        let _ = MatZq::new(1, 0, 3);
209    }
210
211    /// Ensure that a new zero matrix fails with `0` as `num_rows`.
212    #[should_panic]
213    #[test]
214    fn error_zero_num_rows() {
215        let _ = MatZq::new(0, 1, 3);
216    }
217
218    /// Ensure that an invalid modulus yields an error.
219    #[should_panic]
220    #[test]
221    fn invalid_modulus_error() {
222        let _ = MatZq::new(2, 2, 1);
223    }
224
225    /// Ensure that a negative modulus yields an error.
226    #[should_panic]
227    #[test]
228    fn negative_modulus_error() {
229        let _ = MatZq::new(2, 2, -3);
230    }
231
232    /// Ensure that a negative modulus yields an error.
233    #[should_panic]
234    #[test]
235    fn one_modulus_error() {
236        let _ = MatZq::new(2, 2, 1);
237    }
238}