qfall_math/integer_mod_q/modulus_polynomial_ring_zq/get.rs
1// Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann, 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//! Implementations to get information about a [`ModulusPolynomialRingZq].
10
11use crate::{
12 integer::{PolyOverZ, Z},
13 integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolyOverZq, Zq},
14 traits::GetCoefficient,
15};
16use flint_sys::{
17 fmpz::fmpz_init_set,
18 fmpz_mod::fmpz_mod_ctx_init,
19 fmpz_mod_poly::fmpz_mod_poly_get_coeff_fmpz,
20 fq::{fq_ctx_degree, fq_ctx_struct},
21};
22use std::{mem::MaybeUninit, rc::Rc};
23
24impl GetCoefficient<Zq> for ModulusPolynomialRingZq {
25 /// Returns the coefficient of a polynomial [`ModulusPolynomialRingZq`] as a [`Zq`].
26 ///
27 /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
28 ///
29 /// Parameters:
30 /// - `index`: the index of the coefficient to get (has to be positive)
31 ///
32 /// Returns the coefficient as a [`Zq`].
33 ///
34 /// # Examples
35 /// ```
36 /// use qfall_math::traits::*;
37 /// use qfall_math::integer_mod_q::{Zq, ModulusPolynomialRingZq};
38 /// use std::str::FromStr;
39 ///
40 /// let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
41 ///
42 /// let coeff_0: Zq = poly.get_coeff(0).unwrap();
43 /// let coeff_1: Zq = unsafe{ poly.get_coeff_unchecked(1) };
44 /// let coeff_4: Zq = poly.get_coeff(4).unwrap();
45 ///
46 /// assert_eq!(Zq::from((0, 17)), coeff_0);
47 /// assert_eq!(Zq::from((1, 17)), coeff_1);
48 /// assert_eq!(Zq::from((0, 17)), coeff_4);
49 /// ```
50 ///
51 /// # Safety
52 /// To use this function safely, make sure that the selected index
53 /// is greater or equal than `0`.
54 unsafe fn get_coeff_unchecked(&self, index: i64) -> Zq {
55 let out_z: Z = unsafe { self.get_coeff_unchecked(index) };
56
57 let mut ctx = MaybeUninit::uninit();
58 unsafe {
59 fmpz_mod_ctx_init(ctx.as_mut_ptr(), &self.get_fq_ctx().ctxp[0].n[0]);
60
61 let modulus = Modulus {
62 modulus: Rc::new(ctx.assume_init()),
63 };
64
65 Zq::from((out_z, modulus))
66 }
67 }
68}
69
70impl GetCoefficient<Z> for ModulusPolynomialRingZq {
71 /// Returns the coefficient of a polynomial [`ModulusPolynomialRingZq`] as a [`Z`].
72 ///
73 /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
74 ///
75 /// Parameters:
76 /// - `index`: the index of the coefficient to get (has to be positive)
77 ///
78 /// Returns the coefficient as a [`Z`], or a [`MathError`](crate::error::MathError) if the provided index
79 /// is negative and therefore invalid, or it does not fit into an [`i64`].
80 ///
81 /// # Examples
82 /// ```
83 /// use qfall_math::traits::*;
84 /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
85 /// use qfall_math::integer::Z;
86 /// use std::str::FromStr;
87 ///
88 /// let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
89 ///
90 /// let coeff_0: Z = poly.get_coeff(0).unwrap();
91 /// let coeff_1: Z = unsafe{ poly.get_coeff_unchecked(1) };
92 /// let coeff_4: Z = poly.get_coeff(4).unwrap();
93 ///
94 /// assert_eq!(Z::ZERO, coeff_0);
95 /// assert_eq!(Z::ONE, coeff_1);
96 /// assert_eq!(Z::ZERO, coeff_4);
97 /// ```
98 ///
99 /// # Safety
100 /// To use this function safely, make sure that the selected index
101 /// is greater or equal than `0`.
102 unsafe fn get_coeff_unchecked(&self, index: i64) -> Z {
103 let mut out = Z::default();
104
105 unsafe {
106 fmpz_mod_poly_get_coeff_fmpz(
107 &mut out.value,
108 &self.modulus.modulus[0],
109 index,
110 &self.get_fq_ctx().ctxp[0],
111 )
112 }
113
114 out
115 }
116}
117
118impl ModulusPolynomialRingZq {
119 /// Returns the [`fq_ctx_struct`] of a modulus and is only used internally.
120 pub(crate) fn get_fq_ctx(&self) -> &fq_ctx_struct {
121 self.modulus.as_ref()
122 }
123
124 /// Returns the context integer as a [`Z`].
125 ///
126 /// # Examples
127 /// ```
128 /// use qfall_math::integer::Z;
129 /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
130 /// use std::str::FromStr;
131 ///
132 /// let modulus_ring = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
133 ///
134 /// let modulus = modulus_ring.get_q();
135 ///
136 /// let cmp_modulus = Z::from(17);
137 /// assert_eq!(cmp_modulus, modulus);
138 /// ```
139 pub fn get_q(&self) -> Z {
140 let mut out = Z::default();
141 unsafe {
142 fmpz_init_set(&mut out.value, &self.get_fq_ctx().ctxp[0].n[0]);
143 }
144 out
145 }
146
147 /// Returns the degree of a polynomial [`ModulusPolynomialRingZq`] as a [`i64`].
148 /// The zero polynomial has degree `-1`.
149 ///
150 /// # Examples
151 /// ```
152 /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
153 /// use std::str::FromStr;
154 ///
155 /// let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 7").unwrap();
156 ///
157 /// let degree = poly.get_degree(); // This would only return 3
158 /// ```
159 pub fn get_degree(&self) -> i64 {
160 unsafe { fq_ctx_degree(self.get_fq_ctx()) }
161 }
162
163 /// Returns a representative polynomial of the [`ModulusPolynomialRingZq`] element.
164 ///
165 /// The representation of the coefficients is in the range `[0, modulus)`.
166 ///
167 /// # Examples
168 /// ```
169 /// use qfall_math::integer::PolyOverZ;
170 /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
171 /// use std::str::FromStr;
172 ///
173 /// let modulus = ModulusPolynomialRingZq::from_str("4 -3 0 31 1 mod 17").unwrap();
174 ///
175 /// let poly_z = modulus.get_representative_least_nonnegative_residue();
176 ///
177 /// let cmp_poly = PolyOverZ::from_str("4 14 0 14 1").unwrap();
178 /// assert_eq!(cmp_poly, poly_z);
179 /// ```
180 pub fn get_representative_least_nonnegative_residue(&self) -> PolyOverZ {
181 let poly_zq = PolyOverZq::from(self);
182 poly_zq.get_representative_least_nonnegative_residue()
183 }
184}
185
186#[cfg(test)]
187mod test_get_coeff_z {
188 use crate::{
189 integer::Z,
190 integer_mod_q::{ModulusPolynomialRingZq, Zq},
191 traits::GetCoefficient,
192 };
193 use std::str::FromStr;
194
195 /// Ensure that `0` is returned if the provided index is not yet set.
196 #[test]
197 fn index_out_of_range() {
198 let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
199
200 let zero_coeff_1: Z = poly.get_coeff(4).unwrap();
201 let zero_coeff_2 = poly.get_coeff(4).unwrap();
202
203 assert_eq!(0, zero_coeff_1);
204 assert_eq!(Zq::from((0, 17)), zero_coeff_2);
205 }
206
207 /// Tests if coefficients are returned correctly.
208 #[test]
209 fn positive_coeff() {
210 let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
211
212 let coeff_1: Z = poly.get_coeff(2).unwrap();
213 let coeff_2 = poly.get_coeff(2).unwrap();
214
215 assert_eq!(2, coeff_1);
216 assert_eq!(Zq::from((2, 17)), coeff_2);
217 }
218
219 /// Tests if large coefficients are returned correctly.
220 #[test]
221 fn large_coeff() {
222 let poly =
223 ModulusPolynomialRingZq::from_str(&format!("2 1 {} mod {}", u64::MAX - 1, u64::MAX))
224 .unwrap();
225
226 let coefficient_1: Z = poly.get_coeff(1).unwrap();
227 let coefficient_2: Zq = poly.get_coeff(1).unwrap();
228
229 assert_eq!(u64::MAX - 1, coefficient_1);
230 assert_eq!(Zq::from((u64::MAX - 1, u64::MAX)), coefficient_2);
231 }
232
233 /// Tests if negative index yields an error in get_coeff with [`Z`].
234 #[test]
235 #[should_panic]
236 fn negative_index_error_z() {
237 let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
238
239 let _: Z = poly.get_coeff(-1).unwrap();
240 }
241
242 /// Tests if negative index yields an error in get_coeff with [`Zq`].
243 #[test]
244 #[should_panic]
245 fn negative_index_error_zq() {
246 let poly = ModulusPolynomialRingZq::from_str("4 0 1 2 3 mod 17").unwrap();
247
248 let _: Zq = poly.get_coeff(-1).unwrap();
249 }
250}
251
252#[cfg(test)]
253mod test_get_degree {
254 use crate::integer_mod_q::ModulusPolynomialRingZq;
255 use std::str::FromStr;
256
257 /// Ensures that degree is working for constant polynomials.
258 #[test]
259 fn degree_constant() {
260 let degrees = [1, 3, 7, 15, 32, 120];
261 for degree in degrees {
262 let modulus_ring = ModulusPolynomialRingZq::from_str(&format!(
263 "{} {}2 mod 17",
264 degree + 1,
265 "0 ".repeat(degree)
266 ))
267 .unwrap();
268
269 assert_eq!(degree as i64, modulus_ring.get_degree());
270 }
271 }
272
273 /// Ensure that degree is working for polynomials with leading 0 coefficients.
274 #[test]
275 fn degree_leading_zeros() {
276 let poly = ModulusPolynomialRingZq::from_str("4 2 1 0 0 mod 199").unwrap();
277
278 let deg = poly.get_degree();
279
280 assert_eq!(1, deg);
281 }
282
283 /// Ensure that degree is working for polynomials with many coefficients
284 /// flint does not reduce the exponent due to computational cost.
285 #[test]
286 fn degree_many_coefficients() {
287 let poly_1 = ModulusPolynomialRingZq::from_str("7 1 2 3 4 8 1 3 mod 2").unwrap();
288 let poly_2 = ModulusPolynomialRingZq::from_str(&format!(
289 "7 1 2 3 4 8 {} {} mod {}",
290 u64::MAX,
291 i64::MAX,
292 u128::MAX
293 ))
294 .unwrap();
295
296 let deg_1 = poly_1.get_degree();
297 let deg_2 = poly_2.get_degree();
298
299 assert_eq!(6, deg_1);
300 assert_eq!(6, deg_2);
301 }
302}
303
304#[cfg(test)]
305mod test_get_q {
306 use crate::{integer::Z, integer_mod_q::ModulusPolynomialRingZq};
307 use std::str::FromStr;
308
309 /// Ensure that the same modulus is correctly returned for a large modulus.
310 #[test]
311 fn correct_large() {
312 let large_prime = u64::MAX - 58;
313 let modulus_ring =
314 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {large_prime}")).unwrap();
315
316 let modulus = modulus_ring.get_q();
317
318 let cmp_modulus = Z::from(large_prime);
319 assert_eq!(cmp_modulus, modulus);
320 }
321}
322
323#[cfg(test)]
324mod test_get_representative_least_nonnegative_residue {
325 use crate::{
326 integer::PolyOverZ,
327 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
328 };
329 use std::str::FromStr;
330
331 /// Ensure that the getter works for large entries.
332 #[test]
333 fn large_positive() {
334 let large_prime = u64::MAX - 58;
335 let modulus =
336 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {large_prime}")).unwrap();
337 let poly = PolyOverZ::from_str("4 -1 0 1 1").unwrap();
338 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
339
340 let poly_z = poly_ring.get_representative_least_nonnegative_residue();
341
342 let cmp_poly = PolyOverZ::from_str(&format!("3 {} 0 1", u64::MAX - 60)).unwrap();
343 assert_eq!(cmp_poly, poly_z);
344 }
345}