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}