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