qfall_math/integer_mod_q/mat_zq/
ownership.rs

1// Copyright © 2023 Niklas Siemer
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//! This module contains implementations of functions
10//! important for ownership such as the [`Clone`] and [`Drop`] trait.
11//!
12//! The explicit functions contain the documentation.
13
14use super::MatZq;
15use crate::integer::Z;
16use crate::traits::MatrixDimensions;
17use flint_sys::fmpz_mod_mat::{fmpz_mod_mat_clear, fmpz_mod_mat_init_set};
18
19impl Clone for MatZq {
20    /// Clones the given element and returns a deep clone of the [`MatZq`] element.
21    ///
22    /// # Examples
23    /// ```
24    /// use qfall_math::integer_mod_q::MatZq;
25    /// use std::str::FromStr;
26    ///
27    /// let str_1 = "[[1, 2, 3],[4, 5, 6]] mod 4";
28    /// let a = MatZq::from_str(str_1).unwrap();
29    /// let b = a.clone();
30    /// ```
31    fn clone(&self) -> Self {
32        let mut out = MatZq::new(
33            self.get_num_rows(),
34            self.get_num_columns(),
35            Z::from(self.get_mod()),
36        );
37        unsafe {
38            fmpz_mod_mat_init_set(&mut out.matrix, &self.matrix);
39        }
40        out
41    }
42}
43
44impl Drop for MatZq {
45    /// Drops the given [`MatZq`] value and frees the allocated memory.
46    ///
47    /// # Examples
48    /// ```
49    /// use qfall_math::integer_mod_q::MatZq;
50    /// use std::str::FromStr;
51    ///
52    /// let str_1 = "[[1, 2, 3],[4, 5, 6]] mod 4";
53    /// {
54    ///     let a = MatZq::from_str(str_1).unwrap();
55    /// } // as a's scope ends here, it get's dropped
56    /// ```
57    ///
58    /// ```
59    /// use qfall_math::integer_mod_q::MatZq;
60    /// use std::str::FromStr;
61    ///
62    /// let str_1 = "[[1, 2, 3],[4, 5, 6]] mod 4";
63    /// let a = MatZq::from_str(str_1).unwrap();
64    /// drop(a); // explicitly drops a's value
65    /// ```
66    fn drop(&mut self) {
67        unsafe { fmpz_mod_mat_clear(&mut self.matrix) }
68    }
69}
70
71/// Test that the [`Clone`] trait is correctly implemented.
72#[cfg(test)]
73mod test_clone {
74    use super::MatZq;
75    use crate::integer::Z;
76    use crate::traits::{MatrixDimensions, MatrixGetEntry};
77    use std::str::FromStr;
78
79    /// check if a cloned value is still alive after the original value ran out of scope
80    #[test]
81    #[allow(clippy::redundant_clone)]
82    fn keep_alive() {
83        let a: MatZq;
84        let str_1 = "[[1, 2, 3],[3, 4, 5]] mod 6";
85        {
86            let b = MatZq::from_str(str_1).unwrap();
87
88            a = b.clone();
89        }
90
91        assert_eq!(a.get_num_rows(), 2);
92        assert_eq!(a.get_num_columns(), 3);
93
94        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 0, 0).unwrap(), 1);
95        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 0, 1).unwrap(), 2);
96        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 0, 2).unwrap(), 3);
97        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 1, 0).unwrap(), 3);
98        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 1, 1).unwrap(), 4);
99        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 1, 2).unwrap(), 5);
100    }
101
102    /// check whether the cloned entries are stored separately
103    #[test]
104    fn entries_stored_separately() {
105        let a: MatZq;
106        let string = format!("[[{}, {}],[-10, 0]] mod {}", i64::MAX, i64::MIN, u64::MAX);
107        let b = MatZq::from_str(&string).unwrap();
108
109        a = b.clone();
110
111        assert_ne!(
112            MatrixGetEntry::<Z>::get_entry(&a, 0, 0).unwrap().value.0,
113            MatrixGetEntry::<Z>::get_entry(&b, 0, 0).unwrap().value.0
114        );
115        assert_ne!(
116            MatrixGetEntry::<Z>::get_entry(&a, 0, 1).unwrap().value.0,
117            MatrixGetEntry::<Z>::get_entry(&b, 0, 1).unwrap().value.0
118        );
119        assert_ne!(
120            MatrixGetEntry::<Z>::get_entry(&a, 1, 0).unwrap().value.0,
121            MatrixGetEntry::<Z>::get_entry(&b, 1, 0).unwrap().value.0
122        );
123        assert_eq!(
124            MatrixGetEntry::<Z>::get_entry(&a, 1, 1).unwrap().value.0,
125            MatrixGetEntry::<Z>::get_entry(&b, 1, 1).unwrap().value.0
126        ); // kept on stack
127    }
128
129    /// Check if modulus is applied after cloning
130    #[test]
131    #[allow(clippy::redundant_clone)]
132    fn modulus_applied() {
133        let string = String::from("[[1, 2],[4, 5]] mod 4");
134        let b = MatZq::from_str(&string).unwrap();
135
136        let a = b.clone();
137
138        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 1, 1).unwrap(), 1);
139        assert_eq!(MatrixGetEntry::<Z>::get_entry(&a, 1, 0).unwrap(), 0);
140    }
141
142    /// Check if large modulus is stored separately and therefore cloned deeply
143    #[test]
144    fn modulus_storage() {
145        let string = format!("[[{}, {}],[-10, 0]] mod {}", i64::MAX, i64::MIN, u64::MAX);
146        let b = MatZq::from_str(&string).unwrap();
147
148        let a = b.clone();
149
150        assert_ne!(a.matrix.mod_[0].0, b.matrix.mod_[0].0);
151    }
152}
153
154/// Test that the [`Drop`] trait is correctly implemented.
155#[cfg(test)]
156mod test_drop {
157    use super::MatZq;
158    use crate::integer::Z;
159    use crate::traits::MatrixGetEntry;
160    use std::collections::HashSet;
161    use std::str::FromStr;
162
163    /// Creates a matrix with two large entries, drops it and outputs
164    /// the points these two entries were stored in
165    fn create_and_drop_matzq() -> (i64, i64, i64) {
166        let string = format!("[[{}, {}]] mod {}", i64::MAX, i64::MIN, u64::MAX);
167        let a = MatZq::from_str(&string).unwrap();
168
169        let storage_mod = a.matrix.mod_[0].0;
170        let storage_0 = MatrixGetEntry::<Z>::get_entry(&a, 0, 0).unwrap().value.0;
171        let storage_1 = MatrixGetEntry::<Z>::get_entry(&a, 0, 1).unwrap().value.0;
172
173        (storage_mod, storage_0, storage_1)
174    }
175
176    /// Check whether freed memory is reused afterwards
177    #[test]
178    fn free_memory() {
179        let mut set = HashSet::new();
180
181        for _i in 0..5 {
182            let (a, b, c) = create_and_drop_matzq();
183            set.insert(a);
184            set.insert(b);
185            set.insert(c);
186        }
187
188        assert!(set.len() < 15);
189    }
190}