qfall_math/integer/mat_poly_over_z/
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::MatPolyOverZ;
15use crate::traits::MatrixDimensions;
16use flint_sys::fmpz_poly_mat::{fmpz_poly_mat_clear, fmpz_poly_mat_set};
17
18impl Clone for MatPolyOverZ {
19    /// Clones the given element and returns a deep clone of the [`MatPolyOverZ`] element.
20    ///
21    /// # Examples
22    /// ```
23    /// use qfall_math::integer::MatPolyOverZ;
24    /// use std::str::FromStr;
25    ///
26    /// let a = MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap();
27    /// let b = a.clone();
28    /// ```
29    fn clone(&self) -> Self {
30        // we can unwrap since we know, that the number of rows and columns is positive and fits into an [`i64`]
31        let mut clone = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns());
32
33        unsafe { fmpz_poly_mat_set(&mut clone.matrix, &mut self.matrix.to_owned()) }
34
35        clone
36    }
37}
38
39impl Drop for MatPolyOverZ {
40    /// Drops the given [`MatPolyOverZ`] value and frees the allocated memory.
41    ///
42    /// # Examples
43    /// ```
44    /// use qfall_math::integer::MatPolyOverZ;
45    /// use std::str::FromStr;
46    /// {
47    ///     let a = MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap();
48    /// } // as a's scope ends here, it get's dropped
49    /// ```
50    ///
51    /// ```
52    /// use qfall_math::integer::MatPolyOverZ;
53    /// use std::str::FromStr;
54    ///
55    /// let a = MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap();
56    /// drop(a); // explicitly drops a's value
57    /// ```
58    fn drop(&mut self) {
59        unsafe { fmpz_poly_mat_clear(&mut self.matrix) }
60    }
61}
62
63/// Test that the [`Clone`] trait is correctly implemented.
64#[cfg(test)]
65mod test_clone {
66    use crate::integer::MatPolyOverZ;
67    use std::str::FromStr;
68
69    /// check if a clone of a [`MatPolyOverZ`] with an entry larger than 64 bits works
70    #[test]
71    fn large_entries() {
72        let input = format!("[[2  {} -{}]]", u64::MAX, u64::MAX);
73
74        let poly_1 = MatPolyOverZ::from_str(&input).unwrap();
75        let poly_2 = poly_1.clone();
76
77        // tests where the coefficients are stored. Since both are larger than
78        // an i64, both should be a pointer and their values should differ
79        unsafe {
80            assert_ne!(
81                (*(*poly_1.matrix.entries).coeffs.offset(0)).0,
82                (*(*poly_2.matrix.entries).coeffs.offset(0)).0
83            );
84        }
85        unsafe {
86            assert_ne!(
87                (*(*poly_1.matrix.entries).coeffs.offset(1)).0,
88                (*(*poly_2.matrix.entries).coeffs.offset(1)).0
89            );
90        }
91
92        // check if length of polynomial is correctly cloned
93        assert_eq!(unsafe { *poly_1.matrix.entries.offset(0) }.length, 2);
94        assert_eq!(unsafe { *poly_2.matrix.entries.offset(0) }.length, 2);
95
96        assert_eq!(poly_1, poly_2);
97    }
98
99    /// check if several instantiations with small coefficients are cloned correctly
100    #[test]
101    fn small_examples() {
102        let strings = vec!["[[2  0 11]]", "[[2  0 -11]]", "[[2  0 1100]]"];
103
104        for string in strings {
105            let poly_1 = MatPolyOverZ::from_str(string).unwrap();
106
107            let poly_2 = poly_1.clone();
108
109            // Since both coefficients are smaller than an i64,
110            // both should be stored directly on stack and their values should be equal
111            unsafe {
112                assert_eq!(
113                    (*(*poly_1.matrix.entries).coeffs.offset(0)).0,
114                    (*(*poly_2.matrix.entries).coeffs.offset(0)).0
115                );
116            }
117            unsafe {
118                assert_eq!(
119                    (*(*poly_1.matrix.entries).coeffs.offset(1)).0,
120                    (*(*poly_2.matrix.entries).coeffs.offset(1)).0
121                );
122            }
123
124            // check if length of polynomial is correctly cloned
125            assert_eq!(unsafe { *poly_1.matrix.entries.offset(0) }.length, 2);
126            assert_eq!(unsafe { *poly_2.matrix.entries.offset(0) }.length, 2);
127
128            assert_eq!(poly_1, poly_2);
129        }
130    }
131
132    /// Check if a cloned value is still alive after the original value ran out of scope
133    #[test]
134    #[allow(clippy::redundant_clone)]
135    fn keep_alive() {
136        let a: MatPolyOverZ;
137        {
138            let b = MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap();
139            a = b.clone();
140        }
141        assert_eq!(a, MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap());
142    }
143}
144
145/// Test that the [`Drop`] trait is correctly implemented.
146#[cfg(test)]
147mod test_drop {
148    use super::MatPolyOverZ;
149    use std::{collections::HashSet, str::FromStr};
150
151    /// Creates and drops a [`MatPolyOverZ`], and returns the storage points in memory
152    fn create_and_drop_poly_over_z() -> i64 {
153        let a = MatPolyOverZ::from_str(&format!("[[1  {}]]", u64::MAX)).unwrap();
154        unsafe { *(*a.matrix.entries).coeffs.offset(0) }.0
155    }
156
157    /// Check whether freed memory is reused afterwards
158    #[test]
159    fn free_memory() {
160        let mut set = HashSet::new();
161
162        for _i in 0..5 {
163            set.insert(create_and_drop_poly_over_z());
164        }
165
166        assert!(set.capacity() < 5);
167
168        let a = MatPolyOverZ::from_str(&format!("[[2  {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
169        let storage_point = unsafe { *(*a.matrix.entries).coeffs.offset(0) }.0;
170
171        // memory slots differ due to previously created large integer
172        let d = MatPolyOverZ::from_str(&format!("[[2  {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
173        assert_ne!(
174            storage_point,
175            unsafe { *(*d.matrix.entries).coeffs.offset(0) }.0
176        );
177    }
178}