qfall_math/integer/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::Z;
15use flint_sys::fmpz::{fmpz, fmpz_clear, fmpz_init_set};
16
17impl Clone for Z {
18 /// Clones the given element and returns a deep clone of the [`Z`] element.
19 ///
20 /// # Examples
21 /// ```
22 /// use qfall_math::integer::Z;
23 ///
24 /// let a = Z::from(1);
25 /// let b = a.clone();
26 /// ```
27 fn clone(&self) -> Self {
28 // a fresh fmpz value is created, set to the same value as the cloned one,
29 // and wrapped in a new [`Z`] value. Hence, a fresh deep clone is created.
30 let mut value = fmpz(0);
31 unsafe { fmpz_init_set(&mut value, &self.value) };
32 Self { value }
33 }
34}
35
36impl Drop for Z {
37 /// Drops the given [`Z`] value and frees the allocated memory.
38 ///
39 /// # Examples
40 /// ```
41 /// use qfall_math::integer::Z;
42 /// {
43 /// let a = Z::from(3);
44 /// } // as a's scope ends here, it get's dropped
45 /// ```
46 ///
47 /// ```
48 /// use qfall_math::integer::Z;
49 ///
50 /// let a = Z::from(3);
51 /// drop(a); // explicitly drops a's value
52 /// ```
53 fn drop(&mut self) {
54 // According to FLINT's documentation:
55 // "Clears the given fmpz_t, releasing any memory associated with it,
56 // either back to the stack or the OS, depending on whether the reentrant
57 // or non-reentrant version of FLINT is built."
58 // Hence, any memory allocated for values larger than 2^62 is freed. The left
59 // `i64` value is dropped automatically when the variable runs out of scope.
60
61 unsafe { fmpz_clear(&mut self.value) }
62 }
63}
64
65/// Test that the [`Clone`] trait is correctly implemented.
66#[cfg(test)]
67mod test_clone {
68 use super::Z;
69
70 /// check if large positive and negative values are cloned correctly
71 /// additionally check if values are stored at different places in memory
72 #[test]
73 fn large_int() {
74 let max_1 = Z::from(u64::MAX);
75 let min_1 = Z::from(i64::MIN);
76
77 let max_2 = max_1.clone();
78 let min_2 = min_1.clone();
79
80 assert_ne!(max_1.value.0, max_2.value.0);
81 assert_ne!(min_1.value.0, min_2.value.0);
82 assert_eq!(max_1, max_2);
83 assert_eq!(min_1, min_2);
84 }
85
86 /// check if small positive, negative and zero values are cloned correctly
87 /// additionally, check if the values are kept on the stack
88 #[test]
89 fn small_int() {
90 let pos_1 = Z::from(16);
91 let zero_1 = Z::ZERO;
92 let neg_1 = Z::from(-16);
93
94 let pos_2 = pos_1.clone();
95 let zero_2 = zero_1.clone();
96 let neg_2 = neg_1.clone();
97
98 assert_eq!(pos_1.value.0, pos_2.value.0);
99 assert_eq!(zero_1.value.0, zero_2.value.0);
100 assert_eq!(neg_1.value.0, neg_2.value.0);
101 assert_eq!(pos_1, pos_2);
102 assert_eq!(zero_1, zero_2);
103 assert_eq!(neg_1, neg_2);
104 }
105
106 /// check if a cloned value is still alive after the original value ran out of scope
107 #[test]
108 #[allow(clippy::redundant_clone)]
109 fn keep_alive() {
110 let a: Z;
111 {
112 let b = Z::from(5);
113 a = b.clone();
114 }
115 assert_eq!(a, Z::from(5));
116 }
117}
118
119/// Test that the [`Drop`] trait is correctly implemented.
120#[cfg(test)]
121mod test_drop {
122 use super::Z;
123
124 /// Check whether freed memory is reused afterwards
125 #[test]
126 fn free_memory() {
127 let a = Z::from(u64::MAX);
128 let b = Z { value: a.value };
129
130 drop(a);
131
132 // instantiate different [`Z`] value to check if memory slot is reused for different value
133 let c = Z::from(i64::MIN);
134 assert_eq!(c.value.0, b.value.0);
135
136 // memory slots differ due to previously created large integer
137 assert_ne!(b.value.0, Z::from(u64::MAX).value.0);
138 }
139
140 /// This test shows why false copies are a problem, which are prevented for users of the library
141 /// due to attribute privacy of the `value` attribute in [`Z`]
142 #[test]
143 fn memory_equality() {
144 let a = Z::from(u64::MAX);
145 // false clone/ copy of [`Z`] value allows for the [`fmpz`] value to be kept alive
146 // after one reference was dropped and its referenced memory was freed
147 let b = Z { value: a.value };
148
149 drop(a);
150
151 // any large new integer created is filled in the same memory space
152 // as fmpz_equal first checks whether the pointers point to the same point in memory
153 // and are then assumed to be the same, as they both point to the same value, these
154 // values are equal afterwards. Even though, `b` pointed to a different value previously.
155 assert_eq!(b, Z::from(i64::MIN));
156 }
157}