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}