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}