qfall_math/rational/mat_q/
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 crate::traits::MatrixDimensions;
15
16use super::MatQ;
17use flint_sys::fmpq_mat::{fmpq_mat_clear, fmpq_mat_set};
18
19impl Clone for MatQ {
20    /// Clones the given element and returns a deep clone of the given [`MatQ`] element.
21    ///
22    /// # Examples
23    /// ```
24    /// use qfall_math::rational::MatQ;
25    /// use std::str::FromStr;
26    ///
27    /// let string = String::from("[[1/2, 2/3, 3/4],[3/1, 4/2, 5/4]]");
28    /// let a = MatQ::from_str(&string).unwrap();
29    /// let b = a.clone();
30    /// ```
31    fn clone(&self) -> Self {
32        let mut mat = MatQ::new(self.get_num_rows(), self.get_num_columns());
33        unsafe {
34            fmpq_mat_set(&mut mat.matrix, &self.matrix);
35        }
36        mat
37    }
38}
39
40impl Drop for MatQ {
41    /// Drops the given [`MatQ`] value and frees the allocated memory.
42    ///
43    /// # Examples
44    /// ```
45    /// use qfall_math::rational::MatQ;
46    /// use std::str::FromStr;
47    ///
48    /// let string = String::from("[[1/2, 2/3, 3/4],[3/1, 4/2, 5/4]]");
49    /// {
50    ///     let a = MatQ::from_str(&string).unwrap();
51    /// } // as a's scope ends here, it get's dropped
52    /// ```
53    ///
54    /// ```
55    /// use qfall_math::rational::MatQ;
56    /// use std::str::FromStr;
57    ///
58    /// let string = String::from("[[1/2, 2/3, 3/4],[3/1, 4/2, 5/4]]");
59    /// let a = MatQ::from_str(&string).unwrap();
60    /// drop(a); // explicitly drops a's value
61    /// ```
62    fn drop(&mut self) {
63        unsafe { fmpq_mat_clear(&mut self.matrix) }
64    }
65}
66
67/// Test that the [`Clone`] trait is correctly implemented.
68#[cfg(test)]
69mod test_clone {
70    use super::MatQ;
71    use crate::{
72        rational::Q,
73        traits::{MatrixDimensions, MatrixGetEntry},
74    };
75    use std::str::FromStr;
76
77    /// check if a cloned value is still alive after the original value ran out of scope
78    #[test]
79    #[allow(clippy::redundant_clone)]
80    fn keep_alive() {
81        let a: MatQ;
82        let str_1 = "[[1/2, 2/3, 3/4],[3/1, 4/2, 5/4]]";
83        {
84            let b = MatQ::from_str(str_1).unwrap();
85
86            a = b.clone();
87        }
88
89        assert_eq!(a.get_num_rows(), 2);
90        assert_eq!(a.get_num_columns(), 3);
91
92        assert_eq!(a.get_entry(0, 0).unwrap(), Q::from((1, 2)));
93        assert_eq!(a.get_entry(0, 1).unwrap(), Q::from((2, 3)));
94        assert_eq!(a.get_entry(0, 2).unwrap(), Q::from((3, 4)));
95        assert_eq!(a.get_entry(1, 0).unwrap(), Q::from(3));
96        assert_eq!(a.get_entry(1, 1).unwrap(), Q::from((4, 2)));
97        assert_eq!(a.get_entry(1, 2).unwrap(), Q::from((5, 4)));
98    }
99
100    /// check whether the cloned large entries are stored separately
101    #[test]
102    fn entries_stored_separately() {
103        let a: MatQ;
104        let string = format!(
105            "[[{}/1, {}/2],[{}/3, {}/{}]]",
106            u64::MAX,
107            i64::MAX,
108            i64::MIN,
109            u64::MAX,
110            i64::MIN
111        );
112        let b = MatQ::from_str(&string).unwrap();
113
114        a = b.clone();
115
116        assert_ne!(
117            a.get_entry(0, 0).unwrap().value.num.0,
118            b.get_entry(0, 0).unwrap().value.num.0
119        );
120        assert_ne!(
121            a.get_entry(0, 1).unwrap().value.num.0,
122            b.get_entry(0, 1).unwrap().value.num.0
123        );
124        assert_ne!(
125            a.get_entry(1, 0).unwrap().value.num.0,
126            b.get_entry(1, 0).unwrap().value.num.0
127        );
128        assert_ne!(
129            a.get_entry(1, 1).unwrap().value.num.0,
130            b.get_entry(1, 1).unwrap().value.num.0
131        );
132        assert_ne!(
133            a.get_entry(1, 1).unwrap().value.den.0,
134            b.get_entry(1, 1).unwrap().value.den.0
135        );
136
137        assert_eq!(
138            a.get_entry(0, 1).unwrap().value.den.0,
139            b.get_entry(0, 1).unwrap().value.den.0
140        ); // reduction applied, hence kept on stack
141    }
142}
143
144/// Test that the [`Drop`] trait is correctly implemented.
145#[cfg(test)]
146mod test_drop {
147    use super::MatQ;
148    use crate::traits::MatrixGetEntry;
149    use std::collections::HashSet;
150    use std::str::FromStr;
151
152    /// Creates a matrix with two large entries, drops it and outputs
153    /// the points these two entries were stored in
154    fn create_and_drop_matq() -> (i64, i64, i64, i64) {
155        let string = format!("[[{}/{}, {}/{}]]", u64::MAX, i64::MIN, i64::MAX, 1);
156        let a = MatQ::from_str(&string).unwrap();
157
158        let storage_num_0 = a.get_entry(0, 0).unwrap().value.num.0;
159        let storage_num_1 = a.get_entry(0, 1).unwrap().value.num.0;
160        let storage_den_0 = a.get_entry(0, 0).unwrap().value.den.0;
161        let storage_den_1 = a.get_entry(0, 1).unwrap().value.den.0;
162
163        (storage_num_0, storage_num_1, storage_den_0, storage_den_1)
164    }
165
166    /// Check whether freed memory is reused afterwards
167    #[test]
168    fn free_memory() {
169        let mut set = HashSet::new();
170
171        for _i in 0..5 {
172            set.insert(create_and_drop_matq());
173        }
174
175        assert!(set.len() < 5);
176    }
177}