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}