Skip to main content

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}