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