Skip to main content

qfall_math/integer_mod_q/poly_over_zq/
ownership.rs

1// Copyright 2023 Jan 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::PolyOverZq;
15use flint3_sys::{fmpz_mod_poly_clear, fmpz_mod_poly_set};
16
17impl Clone for PolyOverZq {
18    /// Clones the given [`PolyOverZq`] element by returning a deep clone,
19    /// storing the actual value separately and including
20    /// a reference to the [`Modulus`](crate::integer_mod_q::Modulus) element.
21    ///
22    /// # Examples
23    /// ```
24    /// use qfall_math::integer_mod_q::PolyOverZq;
25    /// use std::str::FromStr;
26    ///
27    /// let a = PolyOverZq::from_str("4  0 1 -2 3 mod 13").unwrap();
28    /// let b = a.clone();
29    /// ```
30    fn clone(&self) -> Self {
31        let mut out = PolyOverZq::from(&self.modulus);
32
33        unsafe {
34            fmpz_mod_poly_set(
35                &mut out.poly,
36                &self.poly,
37                self.modulus.get_fmpz_mod_ctx_struct(),
38            )
39        };
40
41        out
42    }
43}
44
45impl Drop for PolyOverZq {
46    /// Drops the given memory allocated for the underlying value
47    /// and frees the allocated memory of the corresponding
48    /// [`Modulus`](crate::integer_mod_q::Modulus) if no other references are left.
49    ///
50    /// # Examples
51    /// ```
52    /// use qfall_math::integer_mod_q::PolyOverZq;
53    /// use std::str::FromStr;
54    /// {
55    ///     let a = PolyOverZq::from_str("4  0 1 -2 3 mod 13").unwrap();
56    /// } // as a's scope ends here, it get's dropped
57    /// ```
58    ///
59    /// ```
60    /// use qfall_math::integer_mod_q::PolyOverZq;
61    /// use std::str::FromStr;
62    ///
63    /// let a = PolyOverZq::from_str("4  0 1 -2 3 mod 13").unwrap();
64    /// drop(a); // explicitly drops a's value
65    /// ```
66    fn drop(&mut self) {
67        unsafe {
68            fmpz_mod_poly_clear(&mut self.poly, self.modulus.get_fmpz_mod_ctx_struct());
69        }
70    }
71}
72
73/// Test that the [`Clone`] trait is correctly implemented.
74#[cfg(test)]
75mod test_clone {
76    use super::PolyOverZq;
77    use std::str::FromStr;
78
79    /// Check if clone points to same point in memory
80    #[test]
81    fn same_reference() {
82        let a = PolyOverZq::from_str(&format!("4  {} 1 -2 3 mod {}", i64::MAX, u64::MAX)).unwrap();
83
84        let b = a.clone();
85
86        // check that Modulus isn't stored twice
87        assert_eq!(
88            a.modulus.get_fmpz_mod_ctx_struct().n[0],
89            b.modulus.get_fmpz_mod_ctx_struct().n[0]
90        );
91
92        // check that values on heap are stored separately
93        assert_ne!(unsafe { *a.poly.coeffs.offset(0) }, unsafe {
94            *b.poly.coeffs.offset(0)
95        }); // heap
96        assert_eq!(unsafe { *a.poly.coeffs.offset(1) }, unsafe {
97            *b.poly.coeffs.offset(1)
98        }); // stack
99        assert_ne!(unsafe { *a.poly.coeffs.offset(2) }, unsafe {
100            *b.poly.coeffs.offset(2)
101        }); // heap
102        assert_eq!(unsafe { *a.poly.coeffs.offset(3) }, unsafe {
103            *b.poly.coeffs.offset(3)
104        }); // stack
105
106        // check if length of polynomials is equal
107        assert_eq!(a.poly.length, b.poly.length);
108    }
109
110    /// Ensure that the zero polynomial can be cloned.
111    #[test]
112    fn zero_polynomial() {
113        let a = PolyOverZq::from_str("0 mod 17").unwrap();
114        let b = PolyOverZq::from_str("1  17 mod 17").unwrap();
115
116        assert_eq!(a, a.clone());
117        assert_eq!(b, b.clone());
118        assert_eq!(a.get_mod(), a.clone().get_mod());
119    }
120
121    /// Ensure that cloning preserves the value and modulus.
122    #[test]
123    fn correct_value() {
124        let a = PolyOverZq::from_str(&format!("4  {} 1 -2 3 mod {}", i64::MAX, u64::MAX)).unwrap();
125
126        let b = a.clone();
127
128        assert_eq!(a, b);
129        assert_eq!(a.get_mod(), b.get_mod());
130    }
131}
132
133#[cfg(test)]
134mod test_drop {
135    use super::PolyOverZq;
136    use std::{collections::HashSet, str::FromStr};
137
138    /// Creates and drops a [`PolyOverZq`] object, and outputs
139    /// the storage point in memory of that [`fmpz_mod_poly`](flint3_sys::fmpz_mod_poly::fmpz_mod_poly_t) struct
140    fn create_and_drop_modulus() -> (i64, i64) {
141        let a = PolyOverZq::from_str(&format!("2  {} -2 mod {}", i64::MAX, u64::MAX)).unwrap();
142
143        (unsafe { *a.poly.coeffs.offset(0) }, unsafe {
144            *a.poly.coeffs.offset(1)
145        })
146    }
147
148    /// Check whether freed memory is reused afterwards
149    #[test]
150    fn free_memory() {
151        let mut storage_addresses = HashSet::new();
152
153        for _i in 0..5 {
154            let (a, b) = create_and_drop_modulus();
155            storage_addresses.insert(a);
156            storage_addresses.insert(b);
157        }
158
159        assert!(storage_addresses.len() < 10);
160    }
161}