qfall_math/integer_mod_q/poly_over_zq/get.rs
1// Copyright 2023 Marvin Beckmann
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 [`PolyOverZq`] polynomial.
10
11use super::PolyOverZq;
12use crate::{
13 integer::{PolyOverZ, Z},
14 integer_mod_q::{Modulus, Zq},
15 traits::{GetCoefficient, SetCoefficient},
16};
17use flint3_sys::{fmpz_mod_poly_degree, fmpz_mod_poly_get_coeff_fmpz, fmpz_mod_poly_get_fmpz_poly};
18
19impl GetCoefficient<Zq> for PolyOverZq {
20 /// Returns the coefficient of a polynomial [`PolyOverZq`] as a [`Zq`].
21 ///
22 /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
23 ///
24 /// Parameters:
25 /// - `index`: the index of the coefficient to get (has to be positive)
26 ///
27 /// Returns the coefficient as a [`Zq`], or a [`MathError`](crate::error::MathError) if the provided index
28 /// is negative and therefore invalid, or it does not fit into an [`i64`].
29 ///
30 /// # Examples
31 /// ```
32 /// use qfall_math::traits::*;
33 /// use qfall_math::integer_mod_q::PolyOverZq;
34 /// use qfall_math::integer_mod_q::Zq;
35 /// use std::str::FromStr;
36 ///
37 /// let poly = PolyOverZq::from_str("4 0 1 2 3 mod 17").unwrap();
38 ///
39 /// let coeff_0: Zq = poly.get_coeff(0).unwrap();
40 /// let coeff_1: Zq = unsafe{ poly.get_coeff_unchecked(1) };
41 /// let coeff_4: Zq = poly.get_coeff(4).unwrap();
42 ///
43 /// assert_eq!(Zq::from((0, 17)), coeff_0);
44 /// assert_eq!(Zq::from((1, 17)), coeff_1);
45 /// assert_eq!(Zq::from((0, 17)), coeff_4);
46 /// ```
47 ///
48 /// # Safety
49 /// To use this function safely, make sure that the selected index
50 /// is greater or equal than `0`.
51 unsafe fn get_coeff_unchecked(&self, index: i64) -> Zq {
52 let out_z: Z = unsafe { self.get_coeff_unchecked(index) };
53 Zq::from((out_z, &self.modulus))
54 }
55}
56
57impl GetCoefficient<Z> for PolyOverZq {
58 /// Returns the coefficient of a polynomial [`PolyOverZq`] as a [`Z`].
59 ///
60 /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
61 ///
62 /// Parameters:
63 /// - `index`: the index of the coefficient to get (has to be positive)
64 ///
65 /// Returns the coefficient as a [`Z`], or a [`MathError`](crate::error::MathError) if the provided index
66 /// is negative and therefore invalid, or it does not fit into an [`i64`].
67 ///
68 /// # Examples
69 /// ```
70 /// use qfall_math::traits::*;
71 /// use qfall_math::integer_mod_q::PolyOverZq;
72 /// use qfall_math::integer::Z;
73 /// use std::str::FromStr;
74 ///
75 /// let poly = PolyOverZq::from_str("4 0 1 2 3 mod 17").unwrap();
76 ///
77 /// let coeff_0: Z = poly.get_coeff(0).unwrap();
78 /// let coeff_1: Z = unsafe{ poly.get_coeff_unchecked(1) };
79 /// let coeff_4: Z = poly.get_coeff(4).unwrap();
80 ///
81 /// assert_eq!(Z::ZERO, coeff_0);
82 /// assert_eq!(Z::ONE, coeff_1);
83 /// assert_eq!(Z::ZERO, coeff_4);
84 /// ```
85 ///
86 /// # Safety
87 /// To use this function safely, make sure that the selected index
88 /// is greater or equal than `0`.
89 unsafe fn get_coeff_unchecked(&self, index: i64) -> Z {
90 let mut out = Z::default();
91 unsafe {
92 fmpz_mod_poly_get_coeff_fmpz(
93 &mut out.value,
94 &self.poly,
95 index,
96 self.modulus.get_fmpz_mod_ctx_struct(),
97 )
98 }
99 out
100 }
101}
102
103impl PolyOverZq {
104 /// Returns the degree of a polynomial [`PolyOverZq`] as a [`i64`].
105 /// The zero polynomial has degree `-1`.
106 ///
107 /// # Examples
108 /// ```
109 /// use qfall_math::integer_mod_q::PolyOverZq;
110 /// use std::str::FromStr;
111 ///
112 /// let poly = PolyOverZq::from_str("4 0 1 2 3 mod 7").unwrap();
113 ///
114 /// let degree = poly.get_degree(); // This would only return 3
115 /// ```
116 pub fn get_degree(&self) -> i64 {
117 unsafe { fmpz_mod_poly_degree(&self.poly, self.modulus.get_fmpz_mod_ctx_struct()) }
118 }
119
120 /// Returns the modulus of the polynomial as a [`Modulus`].
121 ///
122 /// # Examples
123 /// ```
124 /// use qfall_math::integer_mod_q::PolyOverZq;
125 /// use std::str::FromStr;
126 ///
127 /// let matrix = PolyOverZq::from_str("2 1 3 mod 7").unwrap();
128 /// let modulus = matrix.get_mod();
129 /// ```
130 pub fn get_mod(&self) -> Modulus {
131 self.modulus.clone()
132 }
133
134 /// Returns a representative polynomial of the [`PolyOverZq`] element with coefficients centered around `0`.
135 ///
136 /// The output [`PolyOverZ`] has coefficients in the range of `[-modulus/2, modulus/2]`.
137 /// For even moduli, the positive representative is chosen for the element `modulus / 2`.
138 /// Use [`PolyOverZq::get_representative_least_nonnegative_residue`] if they should be
139 /// in the range `[0, modulus)`.
140 ///
141 /// # Examples
142 /// ```
143 /// use qfall_math::integer_mod_q::PolyOverZq;
144 /// use qfall_math::integer::PolyOverZ;
145 /// use std::str::FromStr;
146 ///
147 /// let value = PolyOverZq::from_str("2 10 11 mod 21").unwrap();
148 ///
149 /// let least_abs_residue = value.get_representative_least_absolute_residue();
150 ///
151 /// assert_eq!(PolyOverZ::from_str("2 10 -10").unwrap(), least_abs_residue);
152 /// ```
153 pub fn get_representative_least_absolute_residue(&self) -> PolyOverZ {
154 let mut out = PolyOverZ::default();
155 for i in 0..=self.get_degree() {
156 let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
157 let lar_coeff = coeff.get_representative_least_absolute_residue();
158 unsafe { out.set_coeff_unchecked(i, lar_coeff) };
159 }
160 out
161 }
162
163 /// Returns a representative polynomial of the [`PolyOverZq`] element.
164 ///
165 /// The representation of the coefficients is in the range `[0, modulus)`.
166 /// Use [`PolyOverZq::get_representative_least_absolute_residue`] if they should be
167 /// in the range `[-modulus/2, modulus/2]`.
168 ///
169 /// # Examples
170 /// ```
171 /// use qfall_math::integer::PolyOverZ;
172 /// use qfall_math::integer_mod_q::PolyOverZq;
173 /// use std::str::FromStr;
174 ///
175 /// let poly_zq = PolyOverZq::from_str("4 -3 0 31 1 mod 17").unwrap();
176 ///
177 /// let poly_z = poly_zq.get_representative_least_nonnegative_residue();
178 ///
179 /// let cmp_poly = PolyOverZ::from_str("4 14 0 14 1").unwrap();
180 /// assert_eq!(cmp_poly, poly_z);
181 /// ```
182 pub fn get_representative_least_nonnegative_residue(&self) -> PolyOverZ {
183 let mut out = PolyOverZ::default();
184 unsafe {
185 fmpz_mod_poly_get_fmpz_poly(
186 &mut out.poly,
187 &self.poly,
188 self.modulus.get_fmpz_mod_ctx_struct(),
189 )
190 };
191 out
192 }
193}
194
195// We omit the tests for the value of the [`Zq`], and focus on the [`Modulus`] being set correctly
196// since get_coefficient for [`Z`] is called, where we will check the value itself.
197#[cfg(test)]
198mod test_get_coeff_zq_modulus {
199 use crate::{
200 integer_mod_q::{Modulus, PolyOverZq, Zq},
201 traits::GetCoefficient,
202 };
203 use std::str::FromStr;
204
205 /// Ensure that the [`Modulus`] is transferred correctly when accessing an index out of bounds.
206 #[test]
207 fn index_out_of_range_modulus() {
208 let modulus_str = format!("17{}", u64::MAX);
209 let modulus = Modulus::from_str(&modulus_str).unwrap();
210
211 let poly = PolyOverZq::from_str(&format!("4 0 1 2 3 mod {modulus_str}")).unwrap();
212
213 let zero_coeff: Zq = poly.get_coeff(4).unwrap();
214
215 assert_eq!(modulus, zero_coeff.modulus);
216 }
217
218 /// Ensure that the [`Modulus`] is transferred correctly when accessing an index in bounds.
219 #[test]
220 fn index_in_range_modulus() {
221 let modulus_str = format!("17{}", u64::MAX);
222 let modulus = Modulus::from_str(&modulus_str).unwrap();
223
224 let poly = PolyOverZq::from_str(&format!("4 0 1 2 3 mod {modulus_str}")).unwrap();
225
226 let third_coeff: Zq = poly.get_coeff(3).unwrap();
227
228 assert_eq!(modulus, third_coeff.modulus);
229 }
230}
231
232#[cfg(test)]
233mod test_get_coeff_z {
234 use crate::{integer::Z, integer_mod_q::PolyOverZq, traits::GetCoefficient};
235 use std::str::FromStr;
236
237 /// Ensure that `0` is returned if the provided index is not yet set.
238 #[test]
239 fn index_out_of_range() {
240 let modulus_str = format!("17{}", u64::MAX);
241
242 let poly = PolyOverZq::from_str(&format!("4 0 1 2 3 mod {modulus_str}")).unwrap();
243
244 let zero_coeff: Z = poly.get_coeff(4).unwrap();
245
246 assert_eq!(0, zero_coeff);
247 }
248
249 /// Tests if positive coefficients are returned correctly.
250 #[test]
251 fn positive_coeff() {
252 let modulus_str = format!("17{}", u64::MAX);
253
254 let poly = PolyOverZq::from_str(&format!("4 0 1 2 3 mod {modulus_str}")).unwrap();
255
256 let coeff: Z = poly.get_coeff(2).unwrap();
257
258 assert_eq!(2, coeff);
259 }
260
261 /// Tests if large coefficients are returned correctly.
262 #[test]
263 fn large_coeff() {
264 let modulus_str = format!("17{}", u64::MAX);
265 let large_string = format!("2 {} {} mod {modulus_str}", u64::MAX, i64::MAX);
266 let poly = PolyOverZq::from_str(&large_string).unwrap();
267
268 let coefficint_1: Z = poly.get_coeff(0).unwrap();
269 let coefficint_2: Z = poly.get_coeff(1).unwrap();
270
271 assert_eq!(u64::MAX, coefficint_1);
272 assert_eq!(i64::MAX, coefficint_2);
273 }
274
275 /// Tests if large negative coefficients are returned correctly.
276 #[test]
277 fn large_modulus_applied_negative_large_coefficient() {
278 let modulus_str = format!("{}", u64::MAX);
279 let large_string = format!("2 -{} {} mod {modulus_str}", u64::MAX, i64::MAX);
280 let poly = PolyOverZq::from_str(&large_string).unwrap();
281
282 let coefficint_1: Z = poly.get_coeff(0).unwrap();
283 let coefficint_2: Z = poly.get_coeff(1).unwrap();
284
285 assert_eq!(0, coefficint_1);
286 assert_eq!(i64::MAX, coefficint_2);
287 }
288}
289
290#[cfg(test)]
291mod test_get_degree {
292 use crate::integer_mod_q::PolyOverZq;
293 use std::str::FromStr;
294
295 /// Ensure that degree is working.
296 #[test]
297 fn degree() {
298 let poly = PolyOverZq::from_str("4 0 1 2 3 mod 7").unwrap();
299
300 let deg = poly.get_degree();
301
302 assert_eq!(3, deg);
303 }
304
305 /// Ensure that degree is working for constant polynomials.
306 #[test]
307 fn degree_constant() {
308 let poly_1 = PolyOverZq::from_str("1 1 mod 19").unwrap();
309 let poly_2 = PolyOverZq::from_str("0 mod 19").unwrap();
310
311 let deg_1 = poly_1.get_degree();
312 let deg_2 = poly_2.get_degree();
313
314 assert_eq!(0, deg_1);
315 assert_eq!(-1, deg_2);
316 }
317
318 /// Ensure that degree is working for polynomials with leading 0 coefficients.
319 #[test]
320 fn degree_leading_zeros() {
321 let poly = PolyOverZq::from_str("4 1 0 0 0 mod 199").unwrap();
322
323 let deg = poly.get_degree();
324
325 assert_eq!(0, deg);
326 }
327
328 /// Ensure that degree is working for polynomials with many coefficients
329 /// flint does not reduce the exponent due to computational cost.
330 #[test]
331 fn degree_many_coefficients() {
332 let poly_1 = PolyOverZq::from_str("7 1 2 3 4 8 1 3 mod 2").unwrap();
333 let poly_2 = PolyOverZq::from_str(&format!(
334 "7 1 2 3 4 8 {} {} mod {}",
335 u64::MAX,
336 i64::MAX,
337 u128::MAX
338 ))
339 .unwrap();
340
341 let deg_1 = poly_1.get_degree();
342 let deg_2 = poly_2.get_degree();
343
344 assert_eq!(6, deg_1);
345 assert_eq!(6, deg_2);
346 }
347}
348
349#[cfg(test)]
350mod test_mod {
351 use crate::integer_mod_q::{Modulus, PolyOverZq};
352 use std::str::FromStr;
353
354 /// Ensure that the getter for modulus works correctly.
355 #[test]
356 fn get_mod() {
357 let poly = PolyOverZq::from_str("2 1 2 mod 7").unwrap();
358
359 assert_eq!(poly.get_mod(), Modulus::from(7));
360 }
361
362 /// Ensure that the getter for modulus works with large numbers.
363 #[test]
364 fn get_mod_large() {
365 let poly = PolyOverZq::from_str(&format!("2 1 2 mod {}", u64::MAX)).unwrap();
366
367 assert_eq!(poly.get_mod(), Modulus::from(u64::MAX));
368 }
369}
370
371#[cfg(test)]
372mod test_get_representative_least_absolute_residue {
373 use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};
374 use std::str::FromStr;
375
376 /// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
377 #[test]
378 fn large_numbers() {
379 let poly_zq = PolyOverZq::from_str(&format!(
380 "2 {} {} mod {}",
381 i64::MAX,
382 u64::MAX - 1,
383 u64::MAX
384 ))
385 .unwrap();
386
387 let poly_z = poly_zq.get_representative_least_absolute_residue();
388
389 let cmp_poly = PolyOverZ::from_str(&format!("2 {} -1", i64::MAX)).unwrap();
390 assert_eq!(cmp_poly, poly_z);
391 }
392
393 /// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
394 #[test]
395 fn special_numbers() {
396 let poly_zq = PolyOverZq::from_str("3 10 0 11 mod 21").unwrap();
397
398 let poly_z = poly_zq.get_representative_least_absolute_residue();
399
400 let cmp_poly = PolyOverZ::from_str("3 10 0 -10").unwrap();
401 assert_eq!(cmp_poly, poly_z);
402 }
403}
404
405#[cfg(test)]
406mod test_get_representative_least_nonnegative_residue {
407 use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};
408 use std::str::FromStr;
409
410 /// Ensure that the getter works for large entries.
411 #[test]
412 fn large_positive() {
413 let large_prime = u64::MAX - 58;
414 let poly_zq = PolyOverZq::from_str(&format!("4 -1 0 0 1 mod {large_prime}")).unwrap();
415
416 let poly_z = poly_zq.get_representative_least_nonnegative_residue();
417
418 let cmp_poly = PolyOverZ::from_str(&format!("4 {} 0 0 1", u64::MAX - 59)).unwrap();
419 assert_eq!(cmp_poly, poly_z);
420 }
421}