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