qfall_math/integer/mat_z/
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 crate::traits::MatrixDimensions;
15
16use super::MatZ;
17use flint_sys::fmpz_mat::{fmpz_mat_clear, fmpz_mat_set};
18
19impl Clone for MatZ {
20    /// Clones the given element and returns a deep clone of the [`MatZ`] element.
21    ///
22    /// # Examples
23    /// ```
24    /// use qfall_math::integer::MatZ;
25    /// use std::str::FromStr;
26    ///
27    /// let str_1 = "[[1, 2, 3],[3, 4, 5]]";
28    /// let a = MatZ::from_str(str_1).unwrap();
29    /// let b = a.clone();
30    /// ```
31    fn clone(&self) -> Self {
32        let mut mat = MatZ::new(self.get_num_rows(), self.get_num_columns());
33        unsafe {
34            fmpz_mat_set(&mut mat.matrix, &self.matrix);
35        }
36        mat
37    }
38}
39
40impl Drop for MatZ {
41    /// Drops the given [`MatZ`] value and frees the allocated memory.
42    ///
43    /// # Examples
44    /// ```
45    /// use qfall_math::integer::MatZ;
46    /// use std::str::FromStr;
47    ///
48    /// let str_1 = "[[1, 2, 3],[3, 4, 5]]";
49    /// {
50    ///     let a = MatZ::from_str(str_1).unwrap();
51    /// } // as a's scope ends here, it get's dropped
52    /// ```
53    ///
54    /// ```
55    /// use qfall_math::integer::MatZ;
56    /// use std::str::FromStr;
57    ///
58    /// let str_1 = "[[1, 2, 3],[3, 4, 5]]";
59    /// let a = MatZ::from_str(str_1).unwrap();
60    /// drop(a); // explicitly drops a's value
61    /// ```
62    fn drop(&mut self) {
63        unsafe { fmpz_mat_clear(&mut self.matrix) }
64    }
65}
66
67/// Test that the [`Clone`] trait is correctly implemented.
68#[cfg(test)]
69mod test_clone {
70    use super::MatZ;
71    use crate::traits::{MatrixDimensions, MatrixGetEntry};
72    use std::str::FromStr;
73
74    /// Check if a cloned value is still alive after the original value ran out of scope
75    #[test]
76    #[allow(clippy::redundant_clone)]
77    fn keep_alive() {
78        let a: MatZ;
79        let str_1 = "[[1, 2, 3],[3, 4, 5]]";
80        {
81            let b = MatZ::from_str(str_1).unwrap();
82
83            a = b.clone();
84        }
85
86        assert_eq!(a.get_num_rows(), 2);
87        assert_eq!(a.get_num_columns(), 3);
88
89        assert_eq!(a.get_entry(0, 0).unwrap(), 1);
90        assert_eq!(a.get_entry(0, 1).unwrap(), 2);
91        assert_eq!(a.get_entry(0, 2).unwrap(), 3);
92        assert_eq!(a.get_entry(1, 0).unwrap(), 3);
93        assert_eq!(a.get_entry(1, 1).unwrap(), 4);
94        assert_eq!(a.get_entry(1, 2).unwrap(), 5);
95    }
96
97    /// Check whether the cloned entries are stored separately
98    #[test]
99    fn entries_stored_separately() {
100        let a: MatZ;
101        // entries are 2^65 = 36893488147419103232, hence fmpz values kept on heap
102        let str_1 = "[[36893488147419103232, 36893488147419103232],[36893488147419103232, 36893488147419103232]]";
103        let b = MatZ::from_str(str_1).unwrap();
104
105        a = b.clone();
106
107        assert_ne!(
108            a.get_entry(0, 0).unwrap().value.0,
109            b.get_entry(0, 0).unwrap().value.0
110        );
111        assert_ne!(
112            a.get_entry(0, 1).unwrap().value.0,
113            b.get_entry(0, 1).unwrap().value.0
114        );
115        assert_ne!(
116            a.get_entry(1, 0).unwrap().value.0,
117            b.get_entry(1, 0).unwrap().value.0
118        );
119        assert_ne!(
120            a.get_entry(1, 1).unwrap().value.0,
121            b.get_entry(1, 1).unwrap().value.0
122        );
123    }
124}
125
126/// Test that the [`Drop`] trait is correctly implemented.
127#[cfg(test)]
128mod test_drop {
129    use super::MatZ;
130    use crate::traits::MatrixGetEntry;
131    use std::collections::HashSet;
132    use std::str::FromStr;
133
134    /// Creates a matrix with two entries of size 2^65, drops it and outputs
135    /// the points these two entries were stored in
136    fn create_and_drop_matz() -> (i64, i64, i64) {
137        // entries are 2^65 = 36893488147419103232, hence fmpz values kept on heap
138        let str_1 = "[[36893488147419103232, 36893488147419103232]]";
139        let a = MatZ::from_str(str_1).unwrap();
140
141        let storage_mat = unsafe { (*a.matrix.entries).0 };
142        let storage_0 = a.get_entry(0, 0).unwrap().value.0;
143        let storage_1 = a.get_entry(0, 1).unwrap().value.0;
144
145        (storage_mat, storage_0, storage_1)
146    }
147
148    /// Check whether freed memory is reused afterwards
149    #[test]
150    fn free_memory() {
151        let mut set = HashSet::new();
152
153        for _i in 0..5 {
154            set.insert(create_and_drop_matz());
155        }
156
157        assert!(set.len() < 5);
158    }
159}