qfall_math/integer/mat_poly_over_z/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::MatPolyOverZ;
15use crate::traits::MatrixDimensions;
16use flint3_sys::{fmpz_poly_mat_clear, fmpz_poly_mat_set};
17
18impl Clone for MatPolyOverZ {
19 /// Clones the given element and returns a deep clone of the [`MatPolyOverZ`] element.
20 ///
21 /// # Examples
22 /// ```
23 /// use qfall_math::integer::MatPolyOverZ;
24 /// use std::str::FromStr;
25 ///
26 /// let a = MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap();
27 /// let b = a.clone();
28 /// ```
29 fn clone(&self) -> Self {
30 // we can unwrap since we know, that the number of rows and columns is positive and fits into an [`i64`]
31 let mut clone = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns());
32
33 unsafe { fmpz_poly_mat_set(&mut clone.matrix, &self.matrix) }
34
35 clone
36 }
37}
38
39impl Drop for MatPolyOverZ {
40 /// Drops the given [`MatPolyOverZ`] value and frees the allocated memory.
41 ///
42 /// # Examples
43 /// ```
44 /// use qfall_math::integer::MatPolyOverZ;
45 /// use std::str::FromStr;
46 /// {
47 /// let a = MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap();
48 /// } // as a's scope ends here, it get's dropped
49 /// ```
50 ///
51 /// ```
52 /// use qfall_math::integer::MatPolyOverZ;
53 /// use std::str::FromStr;
54 ///
55 /// let a = MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap();
56 /// drop(a); // explicitly drops a's value
57 /// ```
58 fn drop(&mut self) {
59 unsafe { fmpz_poly_mat_clear(&mut self.matrix) }
60 }
61}
62
63/// Test that the [`Clone`] trait is correctly implemented.
64#[cfg(test)]
65mod test_clone {
66 use crate::integer::MatPolyOverZ;
67 use std::str::FromStr;
68
69 /// check if a clone of a [`MatPolyOverZ`] with an entry larger than 64 bits works
70 #[test]
71 fn large_entries() {
72 let input = format!("[[2 {} -{}]]", u64::MAX, u64::MAX);
73
74 let poly_1 = MatPolyOverZ::from_str(&input).unwrap();
75 let poly_2 = poly_1.clone();
76
77 // tests where the coefficients are stored. Since both are larger than
78 // an i64, both should be a pointer and their values should differ
79 unsafe {
80 assert_ne!(
81 (*(*poly_1.matrix.entries).coeffs.offset(0)),
82 (*(*poly_2.matrix.entries).coeffs.offset(0))
83 );
84 }
85 unsafe {
86 assert_ne!(
87 (*(*poly_1.matrix.entries).coeffs.offset(1)),
88 (*(*poly_2.matrix.entries).coeffs.offset(1))
89 );
90 }
91
92 // check if length of polynomial is correctly cloned
93 assert_eq!(
94 unsafe { std::ptr::read(poly_1.matrix.entries.offset(0)) }.length,
95 2
96 );
97 assert_eq!(
98 unsafe { std::ptr::read(poly_2.matrix.entries.offset(0)) }.length,
99 2
100 );
101
102 assert_eq!(poly_1, poly_2);
103 }
104
105 /// check if several instantiations with small coefficients are cloned correctly
106 #[test]
107 fn small_examples() {
108 let strings = vec!["[[2 0 11]]", "[[2 0 -11]]", "[[2 0 1100]]"];
109
110 for string in strings {
111 let poly_1 = MatPolyOverZ::from_str(string).unwrap();
112
113 let poly_2 = poly_1.clone();
114
115 // Since both coefficients are smaller than an i64,
116 // both should be stored directly on stack and their values should be equal
117 unsafe {
118 assert_eq!(
119 (*(*poly_1.matrix.entries).coeffs.offset(0)),
120 (*(*poly_2.matrix.entries).coeffs.offset(0))
121 );
122 }
123 unsafe {
124 assert_eq!(
125 (*(*poly_1.matrix.entries).coeffs.offset(1)),
126 (*(*poly_2.matrix.entries).coeffs.offset(1))
127 );
128 }
129
130 // check if length of polynomial is correctly cloned
131 assert_eq!(
132 unsafe { std::ptr::read(poly_1.matrix.entries.offset(0)) }.length,
133 2
134 );
135 assert_eq!(
136 unsafe { std::ptr::read(poly_2.matrix.entries.offset(0)) }.length,
137 2
138 );
139
140 assert_eq!(poly_1, poly_2);
141 }
142 }
143
144 /// Check if a cloned value is still alive after the original value ran out of scope
145 #[test]
146 #[allow(clippy::redundant_clone)]
147 fn keep_alive() {
148 let a: MatPolyOverZ;
149 {
150 let b = MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap();
151 a = b.clone();
152 }
153 assert_eq!(a, MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap());
154 }
155}
156
157/// Test that the [`Drop`] trait is correctly implemented.
158#[cfg(test)]
159mod test_drop {
160 use super::MatPolyOverZ;
161 use std::{collections::HashSet, str::FromStr};
162
163 /// Creates and drops a [`MatPolyOverZ`], and returns the storage points in memory
164 fn create_and_drop_poly_over_z() -> i64 {
165 let a = MatPolyOverZ::from_str(&format!("[[1 {}]]", u64::MAX)).unwrap();
166 unsafe { *(*a.matrix.entries).coeffs.offset(0) }
167 }
168
169 /// Check whether freed memory is reused afterwards
170 #[test]
171 fn free_memory() {
172 let mut set = HashSet::new();
173
174 for _i in 0..5 {
175 set.insert(create_and_drop_poly_over_z());
176 }
177
178 assert!(set.capacity() < 5);
179
180 let a = MatPolyOverZ::from_str(&format!("[[2 {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
181 let storage_point = unsafe { *(*a.matrix.entries).coeffs.offset(0) };
182
183 // memory slots differ due to previously created large integer
184 let d = MatPolyOverZ::from_str(&format!("[[2 {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
185 assert_ne!(storage_point, unsafe {
186 *(*d.matrix.entries).coeffs.offset(0)
187 });
188 }
189}