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}