qfall_math/integer_mod_q/modulus/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::Modulus;
15use flint_sys::fmpz_mod::fmpz_mod_ctx_clear;
16use std::rc::Rc;
17
18impl Clone for Modulus {
19 /// Clones the given element and returns another cloned reference
20 /// to the [`fmpz_mod_ctx`](flint_sys::fmpz_mod::fmpz_mod_ctx) element.
21 ///
22 /// # Examples
23 /// ```
24 /// use qfall_math::integer_mod_q::Modulus;
25 /// use std::str::FromStr;
26 ///
27 /// let a = Modulus::from(3);
28 /// let b = a.clone();
29 /// ```
30 fn clone(&self) -> Self {
31 Modulus {
32 modulus: Rc::clone(&self.modulus),
33 }
34 }
35}
36
37impl Drop for Modulus {
38 /// Drops the given reference to the [`fmpz_mod_ctx`](flint_sys::fmpz_mod::fmpz_mod_ctx) element
39 /// and frees the allocated memory if no references are left.
40 ///
41 /// # Examples
42 /// ```
43 /// use qfall_math::integer_mod_q::Modulus;
44 /// use std::str::FromStr;
45 /// {
46 /// let a = Modulus::from(3);
47 /// } // as a's scope ends here, it get's dropped
48 /// ```
49 ///
50 /// ```
51 /// use qfall_math::integer_mod_q::Modulus;
52 /// use std::str::FromStr;
53 ///
54 /// let a = Modulus::from(3);
55 /// drop(a); // explicitly drops a's value
56 /// ```
57 fn drop(&mut self) {
58 if Rc::strong_count(&self.modulus) <= 1 {
59 let mut a = *self.modulus;
60 unsafe {
61 fmpz_mod_ctx_clear(&mut a);
62 }
63 }
64 }
65}
66
67/// Test that the [`Clone`] trait is correctly implemented.
68#[cfg(test)]
69mod test_clone {
70 use super::Modulus;
71 use std::{rc::Rc, str::FromStr};
72
73 /// Check if new references/ cloned Moduli's increase the Rc counter
74 #[test]
75 fn references_increased() {
76 let a = Modulus::from(3);
77 assert_eq!(Rc::strong_count(&a.modulus), 1);
78
79 let b = a.clone();
80
81 assert_eq!(Rc::strong_count(&a.modulus), 2);
82 assert_eq!(Rc::strong_count(&b.modulus), 2);
83
84 let c = b.clone();
85
86 assert_eq!(Rc::strong_count(&a.modulus), 3);
87 assert_eq!(Rc::strong_count(&b.modulus), 3);
88 assert_eq!(Rc::strong_count(&c.modulus), 3);
89 }
90
91 /// Check if clone points to same point in memory
92 #[test]
93 fn same_reference() {
94 let a = Modulus::from_str(&"1".repeat(65)).unwrap();
95
96 let b = a.clone();
97
98 assert_eq!(
99 &a.get_fmpz_mod_ctx_struct().to_owned().n[0].0,
100 &b.get_fmpz_mod_ctx_struct().to_owned().n[0].0
101 );
102 }
103}
104
105#[cfg(test)]
106mod test_drop {
107 use super::Modulus;
108 use std::{collections::HashSet, rc::Rc, str::FromStr};
109
110 /// Check whether references are decreased when dropping instances
111 #[test]
112 fn references_decreased() {
113 let a = Modulus::from(3);
114 assert_eq!(Rc::strong_count(&a.modulus), 1);
115
116 {
117 let b = a.clone();
118
119 assert_eq!(Rc::strong_count(&a.modulus), 2);
120 assert_eq!(Rc::strong_count(&b.modulus), 2);
121 }
122
123 assert_eq!(Rc::strong_count(&a.modulus), 1);
124
125 let b = a.clone();
126 assert_eq!(Rc::strong_count(&a.modulus), 2);
127 assert_eq!(Rc::strong_count(&b.modulus), 2);
128
129 let c = b.clone();
130 assert_eq!(Rc::strong_count(&a.modulus), 3);
131 assert_eq!(Rc::strong_count(&b.modulus), 3);
132 assert_eq!(Rc::strong_count(&c.modulus), 3);
133
134 drop(a);
135 assert_eq!(Rc::strong_count(&b.modulus), 2);
136 }
137
138 /// Creates and drops a [`Modulus`] object, and outputs
139 /// the storage point in memory of that [`Modulus`]
140 fn create_and_drop_modulus() -> i64 {
141 let a = Modulus::from_str(&"1".repeat(65)).unwrap();
142 a.get_fmpz_mod_ctx_struct().n[0].0
143 }
144
145 /// Check whether freed memory is reused afterwards
146 #[test]
147 fn free_memory() {
148 let mut storage_addresses = HashSet::new();
149
150 for _i in 0..5 {
151 storage_addresses.insert(create_and_drop_modulus());
152 }
153
154 assert!(storage_addresses.len() < 5);
155 }
156}