qfall_math/integer/poly_over_z/
ownership.rs

1// Copyright © 2023 Marvin Beckmann
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::PolyOverZ;
15use flint_sys::fmpz_poly::{fmpz_poly_clear, fmpz_poly_set};
16
17impl Clone for PolyOverZ {
18    /// Clones the given element and returns a deep clone of the [`PolyOverZ`] element.
19    ///
20    /// # Examples
21    /// ```
22    /// use qfall_math::integer::PolyOverZ;
23    /// use std::str::FromStr;
24    ///
25    /// let a = PolyOverZ::from_str("3  0 1 2").unwrap();
26    /// let b = a.clone();
27    /// ```
28    fn clone(&self) -> Self {
29        let mut value = PolyOverZ::default();
30
31        unsafe { fmpz_poly_set(&mut value.poly, &self.poly) }
32
33        value
34    }
35}
36
37impl Drop for PolyOverZ {
38    /// Drops the given [`PolyOverZ`] value and frees the allocated memory.
39    ///
40    /// # Examples
41    /// ```
42    /// use qfall_math::integer::PolyOverZ;
43    /// use std::str::FromStr;
44    /// {
45    ///     let a = PolyOverZ::from_str("3  0 1 2").unwrap();
46    /// } // as a's scope ends here, it get's dropped
47    /// ```
48    ///
49    /// ```
50    /// use qfall_math::integer::PolyOverZ;
51    /// use std::str::FromStr;
52    ///
53    /// let a = PolyOverZ::from_str("3  0 1 2").unwrap();
54    /// drop(a); // explicitly drops a's value
55    /// ```
56    fn drop(&mut self) {
57        // According to FLINT's documentation:
58        // "Clears the given polynomial, releasing any memory used. It must be reinitialized in order to be used again."
59
60        unsafe { fmpz_poly_clear(&mut self.poly) }
61    }
62}
63
64/// Test that the [`Clone`] trait is correctly implemented.
65#[cfg(test)]
66mod test_clone {
67    use crate::integer::PolyOverZ;
68    use std::str::FromStr;
69
70    /// Check if a clone of a [`PolyOverZ`] with an entry larger than 64 bits
71    /// works
72    #[test]
73    fn large_entries() {
74        let input = format!("2  {} -{}", u64::MAX, u64::MAX);
75
76        let poly_1 = PolyOverZ::from_str(&input).unwrap();
77        let poly_2 = poly_1.clone();
78
79        // tests where the first coefficient is stored. Since both are larger than
80        // an i64, both should be a pointer and their values should differ
81        unsafe {
82            assert_ne!((*poly_1.poly.coeffs).0, (*poly_2.poly.coeffs).0);
83        }
84        assert_eq!(poly_1.to_string(), poly_2.to_string());
85    }
86
87    /// Check if several instantiations are cloned correctly
88    #[test]
89    fn small_examples() {
90        let pos_1 = PolyOverZ::from_str("2  0 11").unwrap();
91        let zero_1 = PolyOverZ::from_str("2  0 -11").unwrap();
92        let neg_1 = PolyOverZ::from_str("2  0 1100").unwrap();
93
94        let pos_2 = pos_1.clone();
95        let zero_2 = zero_1.clone();
96        let neg_2 = neg_1.clone();
97
98        assert_eq!(pos_1.to_string(), pos_2.to_string());
99        assert_eq!(zero_1.to_string(), zero_2.to_string());
100        assert_eq!(neg_1.to_string(), neg_2.to_string());
101    }
102
103    /// Check if a cloned value is still alive after the original value ran out of scope
104    #[test]
105    #[allow(clippy::redundant_clone)]
106    fn keep_alive() {
107        let a: PolyOverZ;
108        {
109            let b = PolyOverZ::from_str("2  0 1").unwrap();
110            a = b.clone();
111        }
112        assert_eq!("2  0 1", a.to_string());
113    }
114}
115
116/// Test that the [`Drop`] trait is correctly implemented.
117#[cfg(test)]
118mod test_drop {
119    use super::PolyOverZ;
120    use std::{collections::HashSet, str::FromStr};
121
122    /// Creates and drops a [`PolyOverZ`], and returns the storage points in memory
123    fn create_and_drop_poly_over_z() -> i64 {
124        let a = PolyOverZ::from_str("2  36893488147419103232 36893488147419103233").unwrap();
125        unsafe { *a.poly.coeffs }.0
126    }
127
128    /// Check whether freed memory is reused afterwards
129    #[test]
130    fn free_memory() {
131        let mut set = HashSet::new();
132
133        for _i in 0..5 {
134            set.insert(create_and_drop_poly_over_z());
135        }
136
137        assert!(set.len() < 5);
138
139        let a = PolyOverZ::from_str("2  36893488147419103232 36893488147419103233").unwrap();
140        let storage_point = unsafe { *a.poly.coeffs }.0;
141
142        // memory slots differ due to previously created large integer
143        let d = PolyOverZ::from_str("2  36893488147419103232 36893488147419103233").unwrap();
144        assert_ne!(storage_point, unsafe { *d.poly.coeffs }.0);
145    }
146}