qfall_math/integer/mat_poly_over_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 super::MatPolyOverZ;
15use crate::traits::MatrixDimensions;
16use flint_sys::fmpz_poly_mat::{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, &mut self.matrix.to_owned()) }
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)).0,
82 (*(*poly_2.matrix.entries).coeffs.offset(0)).0
83 );
84 }
85 unsafe {
86 assert_ne!(
87 (*(*poly_1.matrix.entries).coeffs.offset(1)).0,
88 (*(*poly_2.matrix.entries).coeffs.offset(1)).0
89 );
90 }
91
92 // check if length of polynomial is correctly cloned
93 assert_eq!(unsafe { *poly_1.matrix.entries.offset(0) }.length, 2);
94 assert_eq!(unsafe { *poly_2.matrix.entries.offset(0) }.length, 2);
95
96 assert_eq!(poly_1, poly_2);
97 }
98
99 /// check if several instantiations with small coefficients are cloned correctly
100 #[test]
101 fn small_examples() {
102 let strings = vec!["[[2 0 11]]", "[[2 0 -11]]", "[[2 0 1100]]"];
103
104 for string in strings {
105 let poly_1 = MatPolyOverZ::from_str(string).unwrap();
106
107 let poly_2 = poly_1.clone();
108
109 // Since both coefficients are smaller than an i64,
110 // both should be stored directly on stack and their values should be equal
111 unsafe {
112 assert_eq!(
113 (*(*poly_1.matrix.entries).coeffs.offset(0)).0,
114 (*(*poly_2.matrix.entries).coeffs.offset(0)).0
115 );
116 }
117 unsafe {
118 assert_eq!(
119 (*(*poly_1.matrix.entries).coeffs.offset(1)).0,
120 (*(*poly_2.matrix.entries).coeffs.offset(1)).0
121 );
122 }
123
124 // check if length of polynomial is correctly cloned
125 assert_eq!(unsafe { *poly_1.matrix.entries.offset(0) }.length, 2);
126 assert_eq!(unsafe { *poly_2.matrix.entries.offset(0) }.length, 2);
127
128 assert_eq!(poly_1, poly_2);
129 }
130 }
131
132 /// Check if a cloned value is still alive after the original value ran out of scope
133 #[test]
134 #[allow(clippy::redundant_clone)]
135 fn keep_alive() {
136 let a: MatPolyOverZ;
137 {
138 let b = MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap();
139 a = b.clone();
140 }
141 assert_eq!(a, MatPolyOverZ::from_str("[[2 0 1],[1 15]]").unwrap());
142 }
143}
144
145/// Test that the [`Drop`] trait is correctly implemented.
146#[cfg(test)]
147mod test_drop {
148 use super::MatPolyOverZ;
149 use std::{collections::HashSet, str::FromStr};
150
151 /// Creates and drops a [`MatPolyOverZ`], and returns the storage points in memory
152 fn create_and_drop_poly_over_z() -> i64 {
153 let a = MatPolyOverZ::from_str(&format!("[[1 {}]]", u64::MAX)).unwrap();
154 unsafe { *(*a.matrix.entries).coeffs.offset(0) }.0
155 }
156
157 /// Check whether freed memory is reused afterwards
158 #[test]
159 fn free_memory() {
160 let mut set = HashSet::new();
161
162 for _i in 0..5 {
163 set.insert(create_and_drop_poly_over_z());
164 }
165
166 assert!(set.capacity() < 5);
167
168 let a = MatPolyOverZ::from_str(&format!("[[2 {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
169 let storage_point = unsafe { *(*a.matrix.entries).coeffs.offset(0) }.0;
170
171 // memory slots differ due to previously created large integer
172 let d = MatPolyOverZ::from_str(&format!("[[2 {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
173 assert_ne!(
174 storage_point,
175 unsafe { *(*d.matrix.entries).coeffs.offset(0) }.0
176 );
177 }
178}