Skip to main content

qfall_math/integer/mat_poly_over_z/
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::MatPolyOverZ;
15use crate::traits::MatrixDimensions;
16use flint3_sys::{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, &self.matrix) }
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)),
82                (*(*poly_2.matrix.entries).coeffs.offset(0))
83            );
84        }
85        unsafe {
86            assert_ne!(
87                (*(*poly_1.matrix.entries).coeffs.offset(1)),
88                (*(*poly_2.matrix.entries).coeffs.offset(1))
89            );
90        }
91
92        // check if length of polynomial is correctly cloned
93        assert_eq!(
94            unsafe { std::ptr::read(poly_1.matrix.entries.offset(0)) }.length,
95            2
96        );
97        assert_eq!(
98            unsafe { std::ptr::read(poly_2.matrix.entries.offset(0)) }.length,
99            2
100        );
101
102        assert_eq!(poly_1, poly_2);
103    }
104
105    /// check if several instantiations with small coefficients are cloned correctly
106    #[test]
107    fn small_examples() {
108        let strings = vec!["[[2  0 11]]", "[[2  0 -11]]", "[[2  0 1100]]"];
109
110        for string in strings {
111            let poly_1 = MatPolyOverZ::from_str(string).unwrap();
112
113            let poly_2 = poly_1.clone();
114
115            // Since both coefficients are smaller than an i64,
116            // both should be stored directly on stack and their values should be equal
117            unsafe {
118                assert_eq!(
119                    (*(*poly_1.matrix.entries).coeffs.offset(0)),
120                    (*(*poly_2.matrix.entries).coeffs.offset(0))
121                );
122            }
123            unsafe {
124                assert_eq!(
125                    (*(*poly_1.matrix.entries).coeffs.offset(1)),
126                    (*(*poly_2.matrix.entries).coeffs.offset(1))
127                );
128            }
129
130            // check if length of polynomial is correctly cloned
131            assert_eq!(
132                unsafe { std::ptr::read(poly_1.matrix.entries.offset(0)) }.length,
133                2
134            );
135            assert_eq!(
136                unsafe { std::ptr::read(poly_2.matrix.entries.offset(0)) }.length,
137                2
138            );
139
140            assert_eq!(poly_1, poly_2);
141        }
142    }
143
144    /// Check if a cloned value is still alive after the original value ran out of scope
145    #[test]
146    #[allow(clippy::redundant_clone)]
147    fn keep_alive() {
148        let a: MatPolyOverZ;
149        {
150            let b = MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap();
151            a = b.clone();
152        }
153        assert_eq!(a, MatPolyOverZ::from_str("[[2  0 1],[1  15]]").unwrap());
154    }
155}
156
157/// Test that the [`Drop`] trait is correctly implemented.
158#[cfg(test)]
159mod test_drop {
160    use super::MatPolyOverZ;
161    use std::{collections::HashSet, str::FromStr};
162
163    /// Creates and drops a [`MatPolyOverZ`], and returns the storage points in memory
164    fn create_and_drop_poly_over_z() -> i64 {
165        let a = MatPolyOverZ::from_str(&format!("[[1  {}]]", u64::MAX)).unwrap();
166        unsafe { *(*a.matrix.entries).coeffs.offset(0) }
167    }
168
169    /// Check whether freed memory is reused afterwards
170    #[test]
171    fn free_memory() {
172        let mut set = HashSet::new();
173
174        for _i in 0..5 {
175            set.insert(create_and_drop_poly_over_z());
176        }
177
178        assert!(set.capacity() < 5);
179
180        let a = MatPolyOverZ::from_str(&format!("[[2  {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
181        let storage_point = unsafe { *(*a.matrix.entries).coeffs.offset(0) };
182
183        // memory slots differ due to previously created large integer
184        let d = MatPolyOverZ::from_str(&format!("[[2  {} {}]]", u64::MAX - 1, u64::MAX)).unwrap();
185        assert_ne!(storage_point, unsafe {
186            *(*d.matrix.entries).coeffs.offset(0)
187        });
188    }
189}