qfall_math/integer_mod_q/poly_over_zq/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::PolyOverZq;
15use crate::integer::PolyOverZ;
16use flint_sys::fmpz_mod_poly::{
17 fmpz_mod_poly_clear, fmpz_mod_poly_init, fmpz_mod_poly_set_fmpz_poly,
18};
19use std::{mem::MaybeUninit, str::FromStr};
20
21impl Clone for PolyOverZq {
22 /// Clones the given [`PolyOverZq`] element by returning a deep clone,
23 /// storing the actual value separately and including
24 /// a reference to the [`Modulus`](crate::integer_mod_q::Modulus) element.
25 ///
26 /// # Examples
27 /// ```
28 /// use qfall_math::integer_mod_q::PolyOverZq;
29 /// use std::str::FromStr;
30 ///
31 /// let a = PolyOverZq::from_str("4 0 1 -2 3 mod 13").unwrap();
32 /// let b = a.clone();
33 /// ```
34 fn clone(&self) -> Self {
35 let string = self.to_string();
36 let poly_over_z = PolyOverZ::from_str(&string).unwrap();
37
38 let mut poly_zq = MaybeUninit::uninit();
39 unsafe {
40 // init new fmpz_mod_poly_struct
41 fmpz_mod_poly_init(poly_zq.as_mut_ptr(), self.modulus.get_fmpz_mod_ctx_struct());
42
43 // set fmpz_mod_poly_struct to actual value
44 let mut poly_zq = poly_zq.assume_init();
45 fmpz_mod_poly_set_fmpz_poly(
46 &mut poly_zq,
47 &poly_over_z.poly,
48 self.modulus.get_fmpz_mod_ctx_struct(),
49 );
50
51 // return clone
52 Self {
53 poly: poly_zq,
54 modulus: self.modulus.clone(),
55 }
56 }
57 }
58}
59
60impl Drop for PolyOverZq {
61 /// Drops the given memory allocated for the underlying value
62 /// and frees the allocated memory of the corresponding
63 /// [`Modulus`](crate::integer_mod_q::Modulus) if no other references are left.
64 ///
65 /// # Examples
66 /// ```
67 /// use qfall_math::integer_mod_q::PolyOverZq;
68 /// use std::str::FromStr;
69 /// {
70 /// let a = PolyOverZq::from_str("4 0 1 -2 3 mod 13").unwrap();
71 /// } // as a's scope ends here, it get's dropped
72 /// ```
73 ///
74 /// ```
75 /// use qfall_math::integer_mod_q::PolyOverZq;
76 /// use std::str::FromStr;
77 ///
78 /// let a = PolyOverZq::from_str("4 0 1 -2 3 mod 13").unwrap();
79 /// drop(a); // explicitly drops a's value
80 /// ```
81 fn drop(&mut self) {
82 unsafe {
83 fmpz_mod_poly_clear(&mut self.poly, self.modulus.get_fmpz_mod_ctx_struct());
84 }
85 }
86}
87
88/// Test that the [`Clone`] trait is correctly implemented.
89#[cfg(test)]
90mod test_clone {
91 use super::PolyOverZq;
92 use std::str::FromStr;
93
94 /// Check if clone points to same point in memory
95 #[test]
96 fn same_reference() {
97 let a = PolyOverZq::from_str(&format!("4 {} 1 -2 3 mod {}", i64::MAX, u64::MAX)).unwrap();
98
99 let b = a.clone();
100
101 // check that Modulus isn't stored twice
102 assert_eq!(
103 a.modulus.get_fmpz_mod_ctx_struct().n[0].0,
104 b.modulus.get_fmpz_mod_ctx_struct().n[0].0
105 );
106
107 // check that values on heap are stored separately
108 assert_ne!(
109 unsafe { *a.poly.coeffs.offset(0) }.0,
110 unsafe { *b.poly.coeffs.offset(0) }.0
111 ); // heap
112 assert_eq!(
113 unsafe { *a.poly.coeffs.offset(1) }.0,
114 unsafe { *b.poly.coeffs.offset(1) }.0
115 ); // stack
116 assert_ne!(
117 unsafe { *a.poly.coeffs.offset(2) }.0,
118 unsafe { *b.poly.coeffs.offset(2) }.0
119 ); // heap
120 assert_eq!(
121 unsafe { *a.poly.coeffs.offset(3) }.0,
122 unsafe { *b.poly.coeffs.offset(3) }.0
123 ); // stack
124
125 // check if length of polynomials is equal
126 assert_eq!(a.poly.length, b.poly.length);
127 }
128}
129
130#[cfg(test)]
131mod test_drop {
132 use super::PolyOverZq;
133 use std::{collections::HashSet, str::FromStr};
134
135 /// Creates and drops a [`PolyOverZq`] object, and outputs
136 /// the storage point in memory of that [`fmpz_mod_poly`](flint_sys::fmpz_mod_poly::fmpz_mod_poly_t) struct
137 fn create_and_drop_modulus() -> (i64, i64) {
138 let a = PolyOverZq::from_str(&format!("2 {} -2 mod {}", i64::MAX, u64::MAX)).unwrap();
139
140 (
141 unsafe { *a.poly.coeffs.offset(0) }.0,
142 unsafe { *a.poly.coeffs.offset(1) }.0,
143 )
144 }
145
146 /// Check whether freed memory is reused afterwards
147 #[test]
148 fn free_memory() {
149 let mut storage_addresses = HashSet::new();
150
151 for _i in 0..5 {
152 let (a, b) = create_and_drop_modulus();
153 storage_addresses.insert(a);
154 storage_addresses.insert(b);
155 }
156
157 assert!(storage_addresses.len() < 10);
158 }
159}