Skip to main content

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