qfall_math/integer_mod_q/mat_zq/
get.rs

1// Copyright © 2023 Marcel Luca Schmidt, Sven Moog
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//! Implementations to get information about a [`MatZq`] matrix.
10
11use super::MatZq;
12use crate::{
13    integer::{MatZ, Z},
14    integer_mod_q::{Modulus, Zq, fmpz_mod_helpers::length},
15    traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix, MatrixSetEntry},
16};
17use flint_sys::{
18    fmpz::{fmpz, fmpz_init_set},
19    fmpz_mat::fmpz_mat_set,
20    fmpz_mod_mat::{
21        fmpz_mod_mat_entry, fmpz_mod_mat_init_set, fmpz_mod_mat_window_clear,
22        fmpz_mod_mat_window_init,
23    },
24};
25use std::mem::MaybeUninit;
26
27impl MatZq {
28    /// Creates a [`MatZ`] where each entry is a representative of the
29    /// equivalence class of each entry from a [`MatZq`].
30    ///
31    /// The values in the output matrix are in the range of `[0, modulus)`.
32    /// Use [`MatZq::get_representative_least_absolute_residue`] if they should be
33    /// in the range `[-modulus/2, modulus/2]`.
34    ///
35    /// Returns the matrix as a [`MatZ`].
36    ///
37    /// # Examples
38    /// ```
39    /// use qfall_math::integer::MatZ;
40    /// use qfall_math::integer_mod_q::MatZq;
41    /// use std::str::FromStr;
42    ///
43    /// let mat_zq = MatZq::from_str("[[1, 2],[3, -1]] mod 5").unwrap();
44    ///
45    /// let mat_z = mat_zq.get_representative_least_nonnegative_residue();
46    ///
47    /// assert_eq!(mat_z.to_string(), "[[1, 2],[3, 4]]");
48    /// ```
49    pub fn get_representative_least_nonnegative_residue(&self) -> MatZ {
50        let mut out = MatZ::new(self.get_num_rows(), self.get_num_columns());
51        unsafe { fmpz_mat_set(&mut out.matrix, &self.matrix.mat[0]) };
52        out
53    }
54
55    /// Creates a [`MatZ`] where each entry is a representative of the
56    /// equivalence class of each entry from a [`MatZq`] with the
57    /// representatives close to `0`.
58    ///
59    /// The values in the output matrix are in the range of `[-modulus/2, modulus/2]`.
60    /// For even moduli, the positive representative is chosen for the element `modulus / 2`.
61    /// Use [`MatZq::get_representative_least_nonnegative_residue`] if they should be
62    /// in the range `[0, modulus)`.
63    ///
64    /// Returns an [`MatZ`] representation of the given matrix with
65    /// representatives chosen close to `0`.
66    ///
67    /// # Examples
68    /// ```
69    /// use qfall_math::integer_mod_q::MatZq;
70    /// use std::str::FromStr;
71    ///
72    /// let mat_zq_1 = MatZq::from_str("[[1,2],[3,4]] mod 5").unwrap();
73    /// let mat_zq_2 = MatZq::from_str("[[1,2],[3,4]] mod 4").unwrap();
74    ///
75    /// let mat_z_1 = mat_zq_1.get_representative_least_absolute_residue();
76    /// let mat_z_2 = mat_zq_2.get_representative_least_absolute_residue();
77    ///
78    /// assert_eq!(mat_z_1.to_string(), "[[1, 2],[-2, -1]]");
79    /// assert_eq!(mat_z_2.to_string(), "[[1, 2],[-1, 0]]");
80    /// ```
81    pub fn get_representative_least_absolute_residue(&self) -> MatZ {
82        let modulus: Z = Z::from(&self.modulus);
83        let modulus_half = modulus.div_floor(2);
84
85        let mut out = MatZ::new(self.get_num_rows(), self.get_num_columns());
86
87        for row in 0..self.get_num_rows() {
88            for column in 0..self.get_num_columns() {
89                let entry: Z = unsafe { self.get_entry_unchecked(row, column) };
90
91                // Not using Zq::distance for performance reasons.
92                if entry > modulus_half {
93                    unsafe { out.set_entry_unchecked(row, column, entry - &modulus) };
94                } else {
95                    unsafe { out.set_entry_unchecked(row, column, entry) };
96                }
97            }
98        }
99
100        out
101    }
102
103    /// Returns the modulus of the matrix as a [`Modulus`].
104    ///
105    /// # Examples
106    /// ```
107    /// use qfall_math::integer_mod_q::MatZq;
108    ///
109    /// let matrix = MatZq::new(5, 10, 7);
110    /// let entry = matrix.get_mod();
111    /// ```
112    pub fn get_mod(&self) -> Modulus {
113        self.modulus.clone()
114    }
115}
116
117impl MatrixGetSubmatrix for MatZq {
118    /// Returns a deep copy of the submatrix defined by the given parameters
119    /// and does not check the provided dimensions.
120    /// There is also a safe version of this function that checks the input.
121    ///
122    /// Parameters:
123    /// `row_1`: the starting row of the submatrix
124    /// `row_2`: the ending row of the submatrix
125    /// `col_1`: the starting column of the submatrix
126    /// `col_2`: the ending column of the submatrix
127    ///
128    /// Returns the submatrix from `(row_1, col_1)` to `(row_2, col_2)`(exclusively).
129    ///
130    /// # Examples
131    /// ```
132    /// use qfall_math::{integer_mod_q::MatZq, traits::MatrixGetSubmatrix};
133    /// use std::str::FromStr;
134    ///
135    /// let mat = MatZq::identity(3, 3, 17);
136    ///
137    /// let sub_mat_1 = mat.get_submatrix(0, 2, 1, 1).unwrap();
138    /// let sub_mat_2 = mat.get_submatrix(0, -1, 1, -2).unwrap();
139    /// let sub_mat_3 = unsafe{mat.get_submatrix_unchecked(0, 3, 1, 2)};
140    ///
141    /// let e_2 = MatZq::from_str("[[0],[1],[0]] mod 17").unwrap();
142    /// assert_eq!(e_2, sub_mat_1);
143    /// assert_eq!(e_2, sub_mat_2);
144    /// assert_eq!(e_2, sub_mat_3);
145    /// ```
146    ///
147    /// # Safety
148    /// To use this function safely, make sure that the selected submatrix is part
149    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
150    /// occur.
151    unsafe fn get_submatrix_unchecked(
152        &self,
153        row_1: i64,
154        row_2: i64,
155        col_1: i64,
156        col_2: i64,
157    ) -> Self {
158        let mut window = MaybeUninit::uninit();
159        // The memory for the elements of window is shared with self.
160        unsafe {
161            fmpz_mod_mat_window_init(
162                window.as_mut_ptr(),
163                &self.matrix,
164                row_1,
165                col_1,
166                row_2,
167                col_2,
168            )
169        };
170        let mut window_copy = MaybeUninit::uninit();
171        unsafe {
172            // Deep clone of the content of the window
173            fmpz_mod_mat_init_set(window_copy.as_mut_ptr(), window.as_ptr());
174            // Clears the matrix window and releases any memory that it uses. Note that
175            // the memory to the underlying matrix that window points to is not freed
176            fmpz_mod_mat_window_clear(window.as_mut_ptr());
177        }
178        MatZq {
179            matrix: unsafe { window_copy.assume_init() },
180            modulus: self.get_mod(),
181        }
182    }
183}
184
185impl MatZq {
186    /// Efficiently collects all [`fmpz`]s in a [`MatZq`] without cloning them.
187    ///
188    /// Hence, the values on the returned [`Vec`] are intended for short-term use
189    /// as the access to [`fmpz`] values could lead to memory leaks or modified values
190    /// once the [`MatZq`] instance was modified or dropped.
191    ///
192    /// # Examples
193    /// ```compile_fail
194    /// use qfall_math::integer_mod_q::MatZq;
195    /// use std::str::FromStr;
196    ///
197    /// let mat = MatZq::from_str("[[1, 2],[3, 4],[5, 6]] mod 3").unwrap();
198    ///
199    /// let fmpz_entries = mat.collect_entries();
200    /// ```
201    pub(crate) fn collect_entries(&self) -> Vec<fmpz> {
202        let mut entries: Vec<fmpz> =
203            Vec::with_capacity((self.get_num_rows() * self.get_num_columns()) as usize);
204
205        for row in 0..self.get_num_rows() {
206            for col in 0..self.get_num_columns() {
207                // efficiently get entry without cloning the entry itself
208                let entry = unsafe { *fmpz_mod_mat_entry(&self.matrix, row, col) };
209                entries.push(entry);
210            }
211        }
212
213        entries
214    }
215
216    /// Computes the lengths of a given vector of [`Z`] values
217    /// considering the [`Modulus`] of `self`.
218    ///
219    /// # Examples
220    /// ```compile_fail
221    /// use qfall_math::integer_mod_q::MatZq;
222    /// use std::str::FromStr;
223    ///
224    /// let mat = MatZq::from_str("[[1, 2],[3, 4]] mod 3").unwrap();
225    ///
226    /// let lengths = mat.collect_lengths();
227    /// ```
228    pub(crate) fn collect_lengths(&self) -> Vec<Z> {
229        let entries_fmpz = self.collect_entries();
230
231        let modulus_fmpz = self.matrix.mod_[0];
232        let mut entry_lengths = Vec::with_capacity(entries_fmpz.len());
233        for value in entries_fmpz {
234            entry_lengths.push(length(&value, &modulus_fmpz));
235        }
236
237        entry_lengths
238    }
239}
240
241impl MatrixDimensions for MatZq {
242    /// Returns the number of rows of the matrix as a [`i64`].
243    ///
244    /// # Examples
245    /// ```
246    /// use qfall_math::integer_mod_q::MatZq;
247    /// use qfall_math::traits::*;
248    ///
249    /// let matrix = MatZq::new(5, 6, 7);
250    /// let rows = matrix.get_num_rows();
251    /// ```
252    fn get_num_rows(&self) -> i64 {
253        self.matrix.mat[0].r
254    }
255
256    /// Returns the number of columns of the matrix as a [`i64`].
257    ///
258    /// # Examples
259    /// ```
260    /// use qfall_math::integer_mod_q::MatZq;
261    /// use qfall_math::traits::*;
262    ///
263    /// let matrix = MatZq::new(5, 6, 7);
264    /// let rows = matrix.get_num_columns();
265    /// ```
266    fn get_num_columns(&self) -> i64 {
267        self.matrix.mat[0].c
268    }
269}
270
271impl MatrixGetEntry<Z> for MatZq {
272    /// Outputs the [`Z`] value of a specific matrix entry
273    /// without checking whether it's part of the matrix.
274    ///
275    /// Parameters:
276    /// - `row`: specifies the row in which the entry is located
277    /// - `column`: specifies the column in which the entry is located
278    ///
279    /// Returns the [`Z`] value of the matrix at the position of the given
280    /// row and column.
281    ///
282    /// # Safety
283    /// To use this function safely, make sure that the selected entry is part
284    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
285    /// occur.
286    ///
287    /// # Examples
288    /// ```rust
289    /// use qfall_math::integer_mod_q::MatZq;
290    /// use qfall_math::traits::MatrixGetEntry;
291    /// use qfall_math::integer::Z;
292    /// use std::str::FromStr;
293    ///
294    /// let matrix = MatZq::from_str("[[1, 2, 3],[4, 5, 6],[7, 8, 9]] mod 10").unwrap();
295    ///
296    /// let entry_1 :Z = unsafe { matrix.get_entry_unchecked(0, 2) };
297    /// let entry_2 :Z = unsafe { matrix.get_entry_unchecked(2, 1) };
298    /// let entry_3 :Z = unsafe { matrix.get_entry_unchecked(2, 1) };
299    ///
300    /// assert_eq!(3, entry_1);
301    /// assert_eq!(8, entry_2);
302    /// assert_eq!(8, entry_3);
303    /// ```
304    unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> Z {
305        let mut out = Z::default();
306        let entry = unsafe { fmpz_mod_mat_entry(&self.matrix, row, column) };
307        unsafe { fmpz_init_set(&mut out.value, entry) };
308
309        out
310    }
311}
312
313impl MatrixGetEntry<Zq> for MatZq {
314    /// Outputs the [`Zq`] value of a specific matrix entry
315    /// without checking whether it's part of the matrix.
316    ///
317    /// Parameters:
318    /// - `row`: specifies the row in which the entry is located
319    /// - `column`: specifies the column in which the entry is located
320    ///
321    /// Returns the [`Zq`] value of the matrix at the position of the given
322    /// row and column.
323    ///
324    /// # Safety
325    /// To use this function safely, make sure that the selected entry is part
326    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
327    /// occur.
328    ///
329    /// # Examples
330    /// ```rust
331    /// use qfall_math::integer_mod_q::{MatZq, Zq};
332    /// use qfall_math::traits::MatrixGetEntry;
333    /// use std::str::FromStr;
334    ///
335    /// let matrix = MatZq::from_str("[[1, 2, 3],[4, 5, 6],[7, 8, 9]] mod 10").unwrap();
336    ///
337    /// assert_eq!(Zq::from((3, 10)), unsafe { matrix.get_entry_unchecked(0, 2) } );
338    /// assert_eq!(Zq::from((8, 10)), unsafe { matrix.get_entry_unchecked(2, 1) } );
339    /// assert_eq!(Zq::from((8, 10)), unsafe { matrix.get_entry_unchecked(2, 1) } );
340    /// ```
341    unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> Zq {
342        let mut out = Zq::from((0, &self.modulus));
343        let entry = unsafe { fmpz_mod_mat_entry(&self.matrix, row, column) };
344        unsafe { fmpz_init_set(&mut out.value.value, entry) };
345
346        out
347    }
348}
349
350#[cfg(test)]
351mod test_get_entry {
352    use super::Zq;
353    use crate::{
354        integer::Z,
355        integer_mod_q::MatZq,
356        traits::{MatrixGetEntry, MatrixSetEntry},
357    };
358    use std::str::FromStr;
359
360    /// Ensure that getting entries works on the edge.
361    #[test]
362    fn get_edges() {
363        let matrix = MatZq::new(5, 10, u64::MAX);
364
365        let entry_1: Z = matrix.get_entry(0, 0).unwrap();
366        let entry_2: Z = matrix.get_entry(4, 9).unwrap();
367
368        assert_eq!(0, entry_1);
369        assert_eq!(0, entry_2);
370    }
371
372    /// Ensure that getting entries works with large numbers.
373    #[test]
374    fn max_int_positive() {
375        let mut matrix = MatZq::new(5, 10, u64::MAX);
376        let value = Z::from(i64::MAX);
377        matrix.set_entry(0, 0, value).unwrap();
378
379        let entry: Z = matrix.get_entry(0, 0).unwrap();
380
381        assert_eq!(i64::MAX, entry);
382    }
383
384    /// Ensure that getting entries works with large numbers (larger than [`i64`]).
385    #[test]
386    fn large_positive() {
387        let mut matrix = MatZq::new(5, 10, u64::MAX);
388        let value = Z::from(u64::MAX - 1);
389        matrix.set_entry(0, 0, value).unwrap();
390
391        let entry: Z = matrix.get_entry(0, 0).unwrap();
392
393        assert_eq!(u64::MAX - 1, entry);
394    }
395
396    /// Ensure that getting entries works with large numbers.
397    #[test]
398    fn max_int_negative() {
399        let mut matrix = MatZq::new(5, 10, u64::MAX);
400        let value = Z::from(-i64::MAX);
401        matrix.set_entry(0, 0, value).unwrap();
402
403        let entry: Z = matrix.get_entry(0, 0).unwrap();
404
405        assert_eq!((u64::MAX as i128 - i64::MAX as i128) as u64, entry);
406    }
407
408    /// Ensure that getting entries works with large numbers (larger than [`i64`]).
409    #[test]
410    fn large_negative() {
411        let mut matrix = MatZq::new(5, 10, u64::MAX);
412        let value = Z::from(-i64::MAX - 1);
413        matrix.set_entry(0, 0, value).unwrap();
414
415        let entry: Z = matrix.get_entry(0, 0).unwrap();
416
417        assert_eq!((u64::MAX as i128 - i64::MAX as i128) as u64 - 1, entry);
418    }
419
420    /// Ensure that a wrong number of rows yields an Error.
421    #[test]
422    fn error_wrong_row() {
423        let matrix: MatZq = MatZq::new(5, 10, 7);
424
425        assert!(MatrixGetEntry::<Z>::get_entry(&matrix, 5, 1).is_err());
426        assert!(MatrixGetEntry::<Z>::get_entry(&matrix, -6, 1).is_err());
427        assert!(MatrixGetEntry::<Zq>::get_entry(&matrix, 5, 1).is_err());
428        assert!(MatrixGetEntry::<Zq>::get_entry(&matrix, -6, 1).is_err());
429    }
430
431    /// Ensure that a wrong number of columns yields an Error.
432    #[test]
433    fn error_wrong_column() {
434        let matrix = MatZq::new(5, 10, 7);
435
436        assert!(MatrixGetEntry::<Z>::get_entry(&matrix, 1, 10).is_err());
437        assert!(MatrixGetEntry::<Z>::get_entry(&matrix, 1, -11).is_err());
438        assert!(MatrixGetEntry::<Zq>::get_entry(&matrix, 1, 10).is_err());
439        assert!(MatrixGetEntry::<Zq>::get_entry(&matrix, 1, -11).is_err());
440    }
441
442    /// Ensure that negative indices return the correct values.
443    #[test]
444    fn negative_indexing() {
445        let matrix = MatZq::from_str("[[1, 2, 3],[4, 5, 6],[7, 8, 9]] mod 10").unwrap();
446
447        let entry_1: Z = matrix.get_entry(-1, -1).unwrap();
448        let entry_2: Z = matrix.get_entry(-1, -2).unwrap();
449        let entry_3: Z = matrix.get_entry(-3, -3).unwrap();
450        let entry_4: Zq = matrix.get_entry(-1, -1).unwrap();
451        let entry_5: Zq = matrix.get_entry(-1, -2).unwrap();
452        let entry_6: Zq = matrix.get_entry(-3, -3).unwrap();
453
454        assert_eq!(9, entry_1);
455        assert_eq!(8, entry_2);
456        assert_eq!(1, entry_3);
457        assert_eq!(Zq::from((9, 10)), entry_4);
458        assert_eq!(Zq::from((8, 10)), entry_5);
459        assert_eq!(Zq::from((1, 10)), entry_6);
460    }
461
462    /// Ensure that the entry is a deep copy and not just a clone of the reference.
463    #[test]
464    fn memory_test() {
465        let mut matrix = MatZq::new(5, 10, u64::MAX);
466        let value = Zq::from((u64::MAX - 1, u64::MAX));
467        matrix.set_entry(1, 1, value).unwrap();
468        let entry: Z = matrix.get_entry(1, 1).unwrap();
469        matrix.set_entry(1, 1, Z::ONE).unwrap();
470
471        assert_eq!(u64::MAX - 1, entry);
472    }
473
474    /// Ensure that no memory leak occurs in get_entry with [`Z`](crate::integer::Z).
475    #[test]
476    fn get_entry_z_memory() {
477        let mut matrix = MatZq::new(5, 10, u64::MAX);
478        matrix.set_entry(1, 1, Z::from(u64::MAX - 3)).unwrap();
479        let _: Z = matrix.get_entry(1, 1).unwrap();
480        matrix.set_entry(2, 2, Z::from(u64::MAX - 10)).unwrap();
481
482        let entry: Z = matrix.get_entry(1, 1).unwrap();
483        let _z = Z::from(u64::MAX);
484
485        assert_eq!(entry, Z::from(u64::MAX - 3));
486    }
487
488    /// Ensure that no memory leak occurs in get_entry with [`Zq`](crate::integer_mod_q::Zq).
489    #[test]
490    fn get_entry_zq_memory() {
491        let mut matrix = MatZq::new(5, 10, u64::MAX);
492        matrix.set_entry(1, 1, Z::from(u64::MAX - 3)).unwrap();
493        let _: Zq = matrix.get_entry(1, 1).unwrap();
494        matrix.set_entry(2, 2, Z::from(u64::MAX - 10)).unwrap();
495
496        let entry: Z = matrix.get_entry(1, 1).unwrap();
497        let _z = Z::from(u64::MAX);
498
499        assert_eq!(entry, Z::from(u64::MAX - 3));
500    }
501
502    /// Ensure that getting entries works with different types.
503    #[test]
504    fn diff_types() {
505        let matrix = MatZq::new(5, 10, u64::MAX);
506
507        let _: Z = matrix.get_entry(0, 0).unwrap();
508        let _: Zq = matrix.get_entry(0, 0).unwrap();
509    }
510}
511
512#[cfg(test)]
513mod test_get_num {
514    use crate::{integer_mod_q::MatZq, traits::MatrixDimensions};
515
516    /// Ensure that the getter for number of rows works correctly.
517    #[test]
518    fn num_rows() {
519        let matrix = MatZq::new(5, 10, 7);
520
521        assert_eq!(matrix.get_num_rows(), 5);
522    }
523
524    /// Ensure that the getter for number of columns works correctly.
525    #[test]
526    fn num_columns() {
527        let matrix = MatZq::new(5, 10, 7);
528
529        assert_eq!(matrix.get_num_columns(), 10);
530    }
531}
532
533#[cfg(test)]
534mod test_get_representative_least_nonnegative_residue {
535    use crate::{
536        integer::Z,
537        integer_mod_q::MatZq,
538        traits::{MatrixDimensions, MatrixGetEntry, MatrixSetEntry},
539    };
540
541    /// Test if the dimensions are taken over correctly
542    #[test]
543    fn dimensions() {
544        let matzq = MatZq::new(15, 17, 13);
545
546        let matz_1 = matzq.get_representative_least_nonnegative_residue();
547
548        assert_eq!(15, matz_1.get_num_rows());
549        assert_eq!(17, matz_1.get_num_columns());
550    }
551
552    /// Test if entries are taken over correctly
553    #[test]
554    fn entries_taken_over_correctly() {
555        let mut matzq = MatZq::new(2, 2, u64::MAX);
556        matzq.set_entry(0, 0, u64::MAX - 58).unwrap();
557        matzq.set_entry(0, 1, -1).unwrap();
558
559        let matz_1 = matzq.get_representative_least_nonnegative_residue();
560
561        assert_eq!(Z::from(u64::MAX - 1), matz_1.get_entry(0, 1).unwrap());
562        assert_eq!(Z::from(u64::MAX - 58), matz_1.get_entry(0, 0).unwrap());
563    }
564}
565
566#[cfg(test)]
567mod test_mod {
568    use crate::integer_mod_q::{MatZq, Modulus};
569
570    /// Ensure that the getter for modulus works correctly.
571    #[test]
572    fn get_mod() {
573        let matrix = MatZq::new(5, 10, 7);
574
575        assert_eq!(matrix.get_mod(), Modulus::from(7));
576    }
577
578    /// Ensure that the getter for modulus works with large numbers.
579    #[test]
580    fn get_mod_large() {
581        let matrix = MatZq::new(5, 10, u64::MAX);
582
583        assert_eq!(matrix.get_mod(), Modulus::from(u64::MAX));
584    }
585
586    /// Ensure that no memory leak occurs in get_mod.
587    #[test]
588    fn get_mod_memory() {
589        let matrix = MatZq::new(5, 10, u64::MAX);
590        let _ = matrix.get_mod();
591        let _ = Modulus::from(u64::MAX - 1);
592
593        let modulus = matrix.get_mod();
594
595        assert_eq!(modulus, Modulus::from(u64::MAX));
596    }
597}
598
599#[cfg(test)]
600mod test_get_vec {
601    use crate::{integer_mod_q::MatZq, traits::MatrixGetSubmatrix};
602    use std::str::FromStr;
603
604    /// Ensure that getting a row works
605    #[test]
606    fn get_row_works() {
607        let matrix = MatZq::from_str(&format!(
608            "[[0, 0, 0],[4, {}, {}]] mod {}",
609            i64::MAX,
610            i64::MIN,
611            u64::MAX
612        ))
613        .unwrap();
614        let row_1 = matrix.get_row(0).unwrap();
615        let row_2 = matrix.get_row(1).unwrap();
616
617        let cmp_1 = MatZq::from_str(&format!("[[0, 0, 0]] mod {}", u64::MAX)).unwrap();
618        let cmp_2 = MatZq::from_str(&format!(
619            "[[4, {}, {}]] mod {}",
620            i64::MAX,
621            i64::MIN,
622            u64::MAX
623        ))
624        .unwrap();
625
626        assert_eq!(cmp_1, row_1);
627        assert_eq!(cmp_2, row_2);
628    }
629
630    /// Ensure that getting a row with a negative index works
631    #[test]
632    fn get_row_negative_indexing_works() {
633        let matrix = MatZq::from_str(&format!(
634            "[[0, 0, 0],[42, {}, {}]] mod {}",
635            i64::MAX,
636            u64::MAX - 1,
637            u64::MAX
638        ))
639        .unwrap();
640        let row_1 = matrix.get_row(-2).unwrap();
641        let row_2 = matrix.get_row(-1).unwrap();
642
643        let cmp_1 = MatZq::from_str(&format!("[[0, 0, 0]] mod {}", u64::MAX)).unwrap();
644        let cmp_2 = MatZq::from_str(&format!(
645            "[[42, {}, {}]] mod {}",
646            i64::MAX,
647            u64::MAX - 1,
648            u64::MAX
649        ))
650        .unwrap();
651
652        assert_eq!(cmp_1, row_1);
653        assert_eq!(cmp_2, row_2);
654    }
655
656    /// Ensure that getting a column works
657    #[test]
658    fn get_column_works() {
659        let matrix = MatZq::from_str(&format!(
660            "[[1, 0, 3],[{}, 0, 5],[{}, 0, 7]] mod {}",
661            i64::MAX,
662            i64::MIN,
663            u64::MAX
664        ))
665        .unwrap();
666        let column_1 = matrix.get_column(0).unwrap();
667        let column_2 = matrix.get_column(1).unwrap();
668        let column_3 = matrix.get_column(2).unwrap();
669
670        let cmp_1 = MatZq::from_str(&format!(
671            "[[1],[{}],[{}]] mod {}",
672            i64::MAX,
673            i64::MIN,
674            u64::MAX
675        ))
676        .unwrap();
677        let cmp_2 = MatZq::from_str(&format!("[[0],[0],[0]] mod {}", u64::MAX)).unwrap();
678        let cmp_3 = MatZq::from_str(&format!("[[3],[5],[7]] mod {}", u64::MAX)).unwrap();
679
680        assert_eq!(cmp_1, column_1);
681        assert_eq!(cmp_2, column_2);
682        assert_eq!(cmp_3, column_3);
683    }
684
685    /// Ensure that getting a column with a negative index works
686    #[test]
687    fn get_column_negative_indexing_works() {
688        let matrix = MatZq::from_str(&format!(
689            "[[42, 0, 42],[{}, 0, 17],[{}, 0, 42]] mod {}",
690            i64::MAX,
691            u64::MAX - 1,
692            u64::MAX
693        ))
694        .unwrap();
695        let column_1 = matrix.get_column(-3).unwrap();
696        let column_2 = matrix.get_column(-2).unwrap();
697        let column_3 = matrix.get_column(-1).unwrap();
698
699        let cmp_1 = MatZq::from_str(&format!(
700            "[[42],[{}],[{}]] mod {}",
701            i64::MAX,
702            u64::MAX - 1,
703            u64::MAX
704        ))
705        .unwrap();
706        let cmp_2 = MatZq::from_str(&format!("[[0],[0],[0]] mod {}", u64::MAX)).unwrap();
707        let cmp_3 = MatZq::from_str(&format!("[[42],[17],[42]] mod {}", u64::MAX)).unwrap();
708
709        assert_eq!(cmp_1, column_1);
710        assert_eq!(cmp_2, column_2);
711        assert_eq!(cmp_3, column_3);
712    }
713
714    /// Ensure that wrong row and column dimensions yields an error
715    #[test]
716    fn wrong_dim_error() {
717        let matrix = MatZq::from_str(&format!(
718            "[[1, 2, 3],[{}, 4, 5],[{}, 6, 7]] mod {}",
719            i64::MAX,
720            i64::MIN,
721            u64::MAX
722        ))
723        .unwrap();
724        let row_1 = matrix.get_row(-4);
725        let row_2 = matrix.get_row(4);
726        let column_1 = matrix.get_column(-4);
727        let column_2 = matrix.get_column(4);
728
729        assert!(row_1.is_err());
730        assert!(row_2.is_err());
731        assert!(column_1.is_err());
732        assert!(column_2.is_err());
733    }
734}
735
736#[cfg(test)]
737mod test_get_submatrix {
738    use crate::{
739        integer::Z,
740        integer_mod_q::MatZq,
741        traits::{MatrixDimensions, MatrixGetSubmatrix},
742    };
743    use std::str::FromStr;
744
745    /// Ensures that getting the entire matrix as a submatrix works.
746    #[test]
747    fn entire_matrix() {
748        let mat = MatZq::identity(5, 5, i64::MAX);
749
750        let sub_mat = mat.get_submatrix(0, 4, 0, 4).unwrap();
751
752        assert_eq!(mat, sub_mat);
753    }
754
755    /// Ensures that a single matrix entry can be retrieved.
756    #[test]
757    fn matrix_single_entry() {
758        let mat = MatZq::identity(5, 5, i64::MAX);
759
760        let sub_mat = mat.get_submatrix(0, 0, 0, 0).unwrap();
761
762        let cmp_mat = MatZq::identity(1, 1, i64::MAX);
763        assert_eq!(cmp_mat, sub_mat);
764    }
765
766    /// Ensures that the dimensions of the submatrix are correct.
767    #[test]
768    fn correct_dimensions() {
769        let mat = MatZq::identity(100, 100, i64::MAX);
770
771        let sub_mat = mat.get_submatrix(1, 37, 0, 29).unwrap();
772
773        assert_eq!(37, sub_mat.get_num_rows());
774        assert_eq!(30, sub_mat.get_num_columns());
775    }
776
777    /// Ensures that a submatrix can be correctly retrieved for a matrix with large
778    /// entries.
779    #[test]
780    fn large_entries() {
781        let mat = MatZq::from_str(&format!(
782            "[[{}, 2, 3],[1, {}, 3]] mod {}",
783            u64::MAX,
784            i64::MIN,
785            u128::MAX
786        ))
787        .unwrap();
788
789        let sub_mat = mat.get_submatrix(0, 1, 0, 1).unwrap();
790
791        let cmp_mat = MatZq::from_str(&format!(
792            "[[{}, 2],[1, {}]] mod {}",
793            u64::MAX,
794            i64::MIN,
795            u128::MAX
796        ))
797        .unwrap();
798        assert_eq!(cmp_mat, sub_mat);
799    }
800
801    /// Ensures that an error is returned if coordinates are addressed that are not
802    /// within the matrix.
803    #[test]
804    fn invalid_coordinates() {
805        let mat = MatZq::identity(10, 10, u64::MAX);
806
807        assert!(mat.get_submatrix(0, 0, 0, 10).is_err());
808        assert!(mat.get_submatrix(0, 10, 0, 0).is_err());
809        assert!(mat.get_submatrix(0, 0, -11, 0).is_err());
810        assert!(mat.get_submatrix(-11, 0, 0, 0).is_err());
811    }
812
813    /// Ensure that negative indices return the correct submatrix.
814    #[test]
815    fn negative_indexing() {
816        let matrix = MatZq::identity(3, 3, u64::MAX);
817
818        assert_eq!(matrix, matrix.get_submatrix(0, -1, 0, -1).unwrap());
819        assert_eq!(matrix, matrix.get_submatrix(-3, -1, -3, -1).unwrap());
820        assert_eq!(
821            matrix.get_row(0).unwrap(),
822            matrix.get_submatrix(0, -3, -3, -1).unwrap()
823        );
824    }
825
826    /// Ensures that the function panics if no columns of the matrix are addressed.
827    #[test]
828    #[should_panic]
829    fn no_columns() {
830        let mat = MatZq::identity(10, 10, u64::MAX);
831
832        let _ = mat.get_submatrix(0, 0, 6, 5);
833    }
834
835    /// Ensures that the function panics if no rows of the matrix are addressed.
836    #[test]
837    #[should_panic]
838    fn no_rows() {
839        let mat = MatZq::identity(10, 10, u64::MAX);
840
841        let _ = mat.get_submatrix(5, 4, 0, 0);
842    }
843
844    /// Ensure that the submatrix function can be called with several types.
845    #[test]
846    fn availability() {
847        let mat = MatZq::identity(10, 10, u64::MAX);
848
849        let _ = mat.get_submatrix(0_i8, 0_i8, 0_i8, 0_i8);
850        let _ = mat.get_submatrix(0_i16, 0_i16, 0_i16, 0_i16);
851        let _ = mat.get_submatrix(0_i32, 0_i32, 0_i32, 0_i32);
852        let _ = mat.get_submatrix(0_i64, 0_i64, 0_i64, 0_i64);
853        let _ = mat.get_submatrix(0_u8, 0_u8, 0_u8, 0_u8);
854        let _ = mat.get_submatrix(0_u16, 0_i16, 0_u16, 0_u16);
855        let _ = mat.get_submatrix(0_u32, 0_i32, 0_u32, 0_u32);
856        let _ = mat.get_submatrix(0_u64, 0_i64, 0_u64, 0_u64);
857        let _ = mat.get_submatrix(&Z::ZERO, &Z::ZERO, &Z::ZERO, &Z::ZERO);
858    }
859}
860
861#[cfg(test)]
862mod test_collect_entries {
863    use super::MatZq;
864    use std::str::FromStr;
865
866    /// Ensures that all entries from the matrices are actually collected in the vector.
867    #[test]
868    fn all_entries_collected() {
869        let mat_1 = MatZq::from_str(&format!(
870            "[[1, 2],[{}, {}],[3, 4]] mod {}",
871            i64::MAX,
872            i64::MIN,
873            u64::MAX
874        ))
875        .unwrap();
876        let mat_2 = MatZq::from_str("[[-1, 2]] mod 2").unwrap();
877
878        let entries_1 = mat_1.collect_entries();
879        let entries_2 = mat_2.collect_entries();
880
881        assert_eq!(entries_1.len(), 6);
882        assert_eq!(entries_1[0].0, 1);
883        assert_eq!(entries_1[1].0, 2);
884        assert!(entries_1[2].0 >= 2_i64.pow(62));
885        assert!(entries_1[3].0 >= 2_i64.pow(62));
886        assert_eq!(entries_1[4].0, 3);
887        assert_eq!(entries_1[5].0, 4);
888
889        assert_eq!(entries_2.len(), 2);
890        assert_eq!(entries_2[0].0, 1);
891        assert_eq!(entries_2[1].0, 0);
892    }
893}
894
895#[cfg(test)]
896mod test_collect_lengths {
897    use super::{MatZq, Z};
898    use std::str::FromStr;
899
900    /// Ensures that the lengths of the matrices are collected correctly.
901    #[test]
902    fn lengths_correctly_computed() {
903        let mat_1 = MatZq::from_str(&format!(
904            "[[1, 2],[{}, {}],[3, 4]] mod {}",
905            i64::MAX - 2,
906            i64::MIN,
907            i64::MAX - 1
908        ))
909        .unwrap();
910        let mat_2 = MatZq::from_str("[[-1, 2]] mod 2").unwrap();
911
912        let lengths_1 = mat_1.collect_lengths();
913        let lengths_2 = mat_2.collect_lengths();
914
915        assert_eq!(lengths_1.len(), 6);
916        assert_eq!(lengths_1[0], Z::ONE);
917        assert_eq!(lengths_1[1], Z::from(2));
918        assert_eq!(lengths_1[2], Z::ONE);
919        assert_eq!(lengths_1[3], Z::from(2));
920        assert_eq!(lengths_1[4], Z::from(3));
921        assert_eq!(lengths_1[5], Z::from(4));
922
923        assert_eq!(lengths_2.len(), 2);
924        assert_eq!(lengths_2[0], Z::ONE);
925        assert_eq!(lengths_2[1], Z::ZERO);
926    }
927}
928
929#[cfg(test)]
930mod test_get_representative_least_absolute_residue {
931    use super::*;
932    use std::str::FromStr;
933
934    /// Test with a large modulus.
935    #[test]
936    fn large_modulus() {
937        let mat_zq = MatZq::from_str(&format!("[[1,2],[-1,-2]] mod {}", u64::MAX)).unwrap();
938
939        let mat_z = mat_zq.get_representative_least_absolute_residue();
940
941        let mat_cmp = MatZ::from_str("[[1,2],[-1,-2]]").unwrap();
942        assert_eq!(mat_z.to_string(), mat_cmp.to_string());
943    }
944
945    /// Test with even modulus.
946    #[test]
947    fn even_modulus() {
948        let mat_zq = MatZq::from_str("[[0,1,2,3,4,5,6,7,8,9,10]] mod 10").unwrap();
949
950        let mat_z = mat_zq.get_representative_least_absolute_residue();
951
952        let mat_cmp = MatZ::from_str("[[0,1,2,3,4,5,-4,-3,-2,-1,0]]").unwrap();
953        assert_eq!(mat_z.to_string(), mat_cmp.to_string());
954    }
955
956    /// Test with uneven modulus.
957    #[test]
958    fn uneven_modulus() {
959        let mat_zq = MatZq::from_str("[[0,1,2,3,4,5,6,7,8,9,10,11]] mod 11").unwrap();
960
961        let mat_z = mat_zq.get_representative_least_absolute_residue();
962
963        let mat_cmp = MatZ::from_str("[[0,1,2,3,4,5,-5,-4,-3,-2,-1,0]]").unwrap();
964        assert_eq!(mat_z.to_string(), mat_cmp.to_string());
965    }
966}