Skip to main content

qfall_math/integer_mod_q/mat_zq/
ownership.rs

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