qfall_math/rational/q/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::Q;
15use flint_sys::fmpq::{fmpq_clear, fmpq_set};
16
17impl Clone for Q {
18 /// Clones the given element and returns another cloned reference
19 /// to the [`fmpq`](flint_sys::fmpq::fmpq) element.
20 ///
21 /// # Examples
22 /// ```
23 /// use qfall_math::rational::Q;
24 /// use std::str::FromStr;
25 ///
26 /// let a = Q::from((3, 4));
27 /// let b = a.clone();
28 /// ```
29 fn clone(&self) -> Self {
30 let mut clone = Self::default();
31 unsafe { fmpq_set(&mut clone.value, &self.value) };
32 clone
33 }
34}
35
36impl Drop for Q {
37 /// Drops the given reference to the [`fmpq`](flint_sys::fmpq::fmpq) element
38 /// and frees the allocated memory if no references are left.
39 ///
40 /// # Examples
41 /// ```
42 /// use qfall_math::rational::Q;
43 /// use std::str::FromStr;
44 /// {
45 /// let a = Q::from((3, 4));
46 /// } // as a's scope ends here, it get's dropped
47 /// ```
48 ///
49 /// ```
50 /// use qfall_math::rational::Q;
51 /// use std::str::FromStr;
52 ///
53 /// let a = Q::from((3, 4));
54 /// drop(a); // explicitly drops a's value
55 /// ```
56 fn drop(&mut self) {
57 unsafe { fmpq_clear(&mut self.value) }
58 }
59}
60
61/// Test that the [`Clone`] trait is correctly implemented.
62#[cfg(test)]
63mod test_clone {
64 use super::Q;
65 use crate::integer::Z;
66 use std::str::FromStr;
67
68 /// check if small positive, negative and zero values are cloned correctly
69 /// additionally, check if the values are kept on the stack
70 #[test]
71 fn clone_equals_small() {
72 let values = ["1/2", "-1/2", "0/1"];
73
74 for str_value in values {
75 let val = Q::from_str(str_value).unwrap();
76 let val_clone = val.clone();
77
78 assert_eq!(
79 Z {
80 value: val.value.num
81 },
82 Z {
83 value: val_clone.value.num
84 }
85 );
86 assert_eq!(
87 Z {
88 value: val.value.den
89 },
90 Z {
91 value: val_clone.value.den
92 }
93 );
94 assert_eq!(val, val_clone);
95
96 // check if cloned values are kept on stack
97 assert_eq!(val.value.num.0, val_clone.value.num.0);
98 assert_eq!(val.value.den.0, val_clone.value.den.0);
99 }
100 }
101
102 /// check if large positive, negative and zero values are cloned correctly
103 /// additionally check if values are stored at different places in memory
104 #[test]
105 fn clone_equals_large() {
106 let large = "1".repeat(65);
107 let signs = ["", "-"];
108
109 for sign in signs {
110 let val = Q::from_str(&format!("{sign}{large}/2")).unwrap();
111 let val_clone = val.clone();
112
113 assert_eq!(
114 Z {
115 value: val.value.num
116 },
117 Z {
118 value: val_clone.value.num
119 }
120 );
121 assert_eq!(
122 Z {
123 value: val.value.den
124 },
125 Z {
126 value: val_clone.value.den
127 }
128 );
129 assert_eq!(val, val_clone);
130
131 // check if point in memory is different from clone
132 assert_ne!(val.value.num.0, val_clone.value.num.0);
133 }
134 }
135
136 /// check if a cloned value is still alive after the original value ran out of scope
137 #[test]
138 #[allow(clippy::redundant_clone)]
139 fn keep_alive() {
140 let a: Q;
141 {
142 let b = Q::from(5);
143 a = b.clone();
144 }
145 assert_eq!(a, Q::from(5));
146 }
147}
148
149/// Test that the [`Drop`] trait is correctly implemented.
150#[cfg(test)]
151mod test_drop {
152 use super::Q;
153 use std::str::FromStr;
154
155 /// Check whether freed memory is reused afterwards
156 #[test]
157 fn free_memory() {
158 let string = format!("{}/2", "1".repeat(65));
159 let a = Q::from_str(&string).unwrap();
160 let num_point_in_memory = a.value.num.0;
161 let den_point_in_memory = a.value.den.0;
162
163 drop(a);
164
165 // instantiate different values to check if memory slot is reused for different values
166 let c = Q::from_str(&string).unwrap();
167 assert_eq!(num_point_in_memory, c.value.num.0);
168 assert_eq!(den_point_in_memory, c.value.den.0);
169
170 // memory slots differ due to previously created large integer
171 assert_ne!(
172 num_point_in_memory,
173 Q::from_str(&"1".repeat(65)).unwrap().value.num.0
174 );
175 }
176}