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}