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 `(-⌈modulus/2⌉, ⌊modulus/2⌋]`,
137    /// i.e. `[-(modulus-1)/2, (modulus-1)/2]` for odd moduli and `(-modulus/2, modulus/2]` for even moduli.
138    /// Hence, the positive representative is chosen for the element `modulus/2` of even moduli.
139    /// Use [`PolyOverZq::get_representative_least_nonnegative_residue`] if they should be
140    /// in the range `[0, modulus)`.
141    ///
142    /// # Examples
143    /// ```
144    /// use qfall_math::integer_mod_q::PolyOverZq;
145    /// use qfall_math::integer::PolyOverZ;
146    /// use std::str::FromStr;
147    ///
148    /// let value = PolyOverZq::from_str("2  10 11 mod 21").unwrap();
149    ///
150    /// let least_abs_residue = value.get_representative_least_absolute_residue();
151    ///
152    /// assert_eq!(PolyOverZ::from_str("2  10 -10").unwrap(), least_abs_residue);
153    /// ```
154    pub fn get_representative_least_absolute_residue(&self) -> PolyOverZ {
155        let mut out = PolyOverZ::default();
156        for i in 0..=self.get_degree() {
157            let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
158            let lar_coeff = coeff.get_representative_least_absolute_residue();
159            unsafe { out.set_coeff_unchecked(i, lar_coeff) };
160        }
161        out
162    }
163
164    /// Returns a representative polynomial of the [`PolyOverZq`] element.
165    ///
166    /// The representation of the coefficients is in the range `[0, modulus)`.
167    /// Use [`PolyOverZq::get_representative_least_absolute_residue`] if they should be
168    /// in the range `(-⌈modulus/2⌉, ⌊modulus/2⌋]`.
169    ///
170    /// # Examples
171    /// ```
172    /// use qfall_math::integer::PolyOverZ;
173    /// use qfall_math::integer_mod_q::PolyOverZq;
174    /// use std::str::FromStr;
175    ///
176    /// let poly_zq = PolyOverZq::from_str("4  -3 0 31 1 mod 17").unwrap();
177    ///
178    /// let poly_z = poly_zq.get_representative_least_nonnegative_residue();
179    ///
180    /// let cmp_poly = PolyOverZ::from_str("4  14 0 14 1").unwrap();
181    /// assert_eq!(cmp_poly, poly_z);
182    /// ```
183    pub fn get_representative_least_nonnegative_residue(&self) -> PolyOverZ {
184        let mut out = PolyOverZ::default();
185        unsafe {
186            fmpz_mod_poly_get_fmpz_poly(
187                &mut out.poly,
188                &self.poly,
189                self.modulus.get_fmpz_mod_ctx_struct(),
190            )
191        };
192        out
193    }
194}
195
196// We omit the tests for the value of the [`Zq`], and focus on the [`Modulus`] being set correctly
197// since get_coefficient for [`Z`] is called, where we will check the value itself.
198#[cfg(test)]
199mod test_get_coeff_zq_modulus {
200    use crate::{
201        integer_mod_q::{Modulus, PolyOverZq, Zq},
202        traits::GetCoefficient,
203    };
204    use std::str::FromStr;
205
206    /// Ensure that the [`Modulus`] is transferred correctly when accessing an index out of bounds.
207    #[test]
208    fn index_out_of_range_modulus() {
209        let modulus_str = format!("17{}", u64::MAX);
210        let modulus = Modulus::from_str(&modulus_str).unwrap();
211
212        let poly = PolyOverZq::from_str(&format!("4  0 1 2 3 mod {modulus_str}")).unwrap();
213
214        let zero_coeff: Zq = poly.get_coeff(4).unwrap();
215
216        assert_eq!(modulus, zero_coeff.modulus);
217    }
218
219    /// Ensure that the [`Modulus`] is transferred correctly when accessing an index in bounds.
220    #[test]
221    fn index_in_range_modulus() {
222        let modulus_str = format!("17{}", u64::MAX);
223        let modulus = Modulus::from_str(&modulus_str).unwrap();
224
225        let poly = PolyOverZq::from_str(&format!("4  0 1 2 3 mod {modulus_str}")).unwrap();
226
227        let third_coeff: Zq = poly.get_coeff(3).unwrap();
228
229        assert_eq!(modulus, third_coeff.modulus);
230    }
231}
232
233#[cfg(test)]
234mod test_get_coeff_z {
235    use crate::{integer::Z, integer_mod_q::PolyOverZq, traits::GetCoefficient};
236    use std::str::FromStr;
237
238    /// Ensure that `0` is returned if the provided index is not yet set.
239    #[test]
240    fn index_out_of_range() {
241        let modulus_str = format!("17{}", u64::MAX);
242
243        let poly = PolyOverZq::from_str(&format!("4  0 1 2 3 mod {modulus_str}")).unwrap();
244
245        let zero_coeff: Z = poly.get_coeff(4).unwrap();
246
247        assert_eq!(0, zero_coeff);
248    }
249
250    /// Tests if positive coefficients are returned correctly.
251    #[test]
252    fn positive_coeff() {
253        let modulus_str = format!("17{}", u64::MAX);
254
255        let poly = PolyOverZq::from_str(&format!("4  0 1 2 3 mod {modulus_str}")).unwrap();
256
257        let coeff: Z = poly.get_coeff(2).unwrap();
258
259        assert_eq!(2, coeff);
260    }
261
262    /// Tests if large coefficients are returned correctly.
263    #[test]
264    fn large_coeff() {
265        let modulus_str = format!("17{}", u64::MAX);
266        let large_string = format!("2  {} {} mod {modulus_str}", u64::MAX, i64::MAX);
267        let poly = PolyOverZq::from_str(&large_string).unwrap();
268
269        let coefficint_1: Z = poly.get_coeff(0).unwrap();
270        let coefficint_2: Z = poly.get_coeff(1).unwrap();
271
272        assert_eq!(u64::MAX, coefficint_1);
273        assert_eq!(i64::MAX, coefficint_2);
274    }
275
276    /// Tests if large negative coefficients are returned correctly.
277    #[test]
278    fn large_modulus_applied_negative_large_coefficient() {
279        let modulus_str = format!("{}", u64::MAX);
280        let large_string = format!("2  -{} {} mod {modulus_str}", u64::MAX, i64::MAX);
281        let poly = PolyOverZq::from_str(&large_string).unwrap();
282
283        let coefficint_1: Z = poly.get_coeff(0).unwrap();
284        let coefficint_2: Z = poly.get_coeff(1).unwrap();
285
286        assert_eq!(0, coefficint_1);
287        assert_eq!(i64::MAX, coefficint_2);
288    }
289}
290
291#[cfg(test)]
292mod test_get_degree {
293    use crate::integer_mod_q::PolyOverZq;
294    use std::str::FromStr;
295
296    /// Ensure that degree is working.
297    #[test]
298    fn degree() {
299        let poly = PolyOverZq::from_str("4  0 1 2 3 mod 7").unwrap();
300
301        let deg = poly.get_degree();
302
303        assert_eq!(3, deg);
304    }
305
306    /// Ensure that degree is working for constant polynomials.
307    #[test]
308    fn degree_constant() {
309        let poly_1 = PolyOverZq::from_str("1  1 mod 19").unwrap();
310        let poly_2 = PolyOverZq::from_str("0 mod 19").unwrap();
311
312        let deg_1 = poly_1.get_degree();
313        let deg_2 = poly_2.get_degree();
314
315        assert_eq!(0, deg_1);
316        assert_eq!(-1, deg_2);
317    }
318
319    /// Ensure that degree is working for polynomials with leading 0 coefficients.
320    #[test]
321    fn degree_leading_zeros() {
322        let poly = PolyOverZq::from_str("4  1 0 0 0 mod 199").unwrap();
323
324        let deg = poly.get_degree();
325
326        assert_eq!(0, deg);
327    }
328
329    /// Ensure that degree is working for polynomials with many coefficients
330    /// flint does not reduce the exponent due to computational cost.
331    #[test]
332    fn degree_many_coefficients() {
333        let poly_1 = PolyOverZq::from_str("7  1 2 3 4 8 1 3 mod 2").unwrap();
334        let poly_2 = PolyOverZq::from_str(&format!(
335            "7  1 2 3 4 8 {} {} mod {}",
336            u64::MAX,
337            i64::MAX,
338            u128::MAX
339        ))
340        .unwrap();
341
342        let deg_1 = poly_1.get_degree();
343        let deg_2 = poly_2.get_degree();
344
345        assert_eq!(6, deg_1);
346        assert_eq!(6, deg_2);
347    }
348}
349
350#[cfg(test)]
351mod test_mod {
352    use crate::integer_mod_q::{Modulus, PolyOverZq};
353    use std::str::FromStr;
354
355    /// Ensure that the getter for modulus works correctly.
356    #[test]
357    fn get_mod() {
358        let poly = PolyOverZq::from_str("2  1 2 mod 7").unwrap();
359
360        assert_eq!(poly.get_mod(), Modulus::from(7));
361    }
362
363    /// Ensure that the getter for modulus works with large numbers.
364    #[test]
365    fn get_mod_large() {
366        let poly = PolyOverZq::from_str(&format!("2  1 2 mod {}", u64::MAX)).unwrap();
367
368        assert_eq!(poly.get_mod(), Modulus::from(u64::MAX));
369    }
370}
371
372#[cfg(test)]
373mod test_get_representative_least_absolute_residue {
374    use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};
375    use std::str::FromStr;
376
377    /// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
378    #[test]
379    fn large_numbers() {
380        let poly_zq = PolyOverZq::from_str(&format!(
381            "2  {} {} mod {}",
382            i64::MAX,
383            u64::MAX - 1,
384            u64::MAX
385        ))
386        .unwrap();
387
388        let poly_z = poly_zq.get_representative_least_absolute_residue();
389
390        let cmp_poly = PolyOverZ::from_str(&format!("2  {} -1", i64::MAX)).unwrap();
391        assert_eq!(cmp_poly, poly_z);
392    }
393
394    /// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
395    #[test]
396    fn special_numbers() {
397        let poly_zq = PolyOverZq::from_str("3  10 0 11 mod 21").unwrap();
398
399        let poly_z = poly_zq.get_representative_least_absolute_residue();
400
401        let cmp_poly = PolyOverZ::from_str("3  10 0 -10").unwrap();
402        assert_eq!(cmp_poly, poly_z);
403    }
404
405    /// Returns the coefficients `0, 1, ..., modulus - 1` and their representatives
406    /// according to `mod±` as defined in FIPS 204 as strings.
407    fn coefficients_and_representatives(modulus: i64) -> (String, String) {
408        let coefficients: Vec<String> = (0..modulus).map(|value| value.to_string()).collect();
409        let representatives: Vec<String> = (0..modulus)
410            .map(|value| {
411                if value > modulus / 2 {
412                    value - modulus
413                } else {
414                    value
415                }
416                .to_string()
417            })
418            .collect();
419        (coefficients.join(" "), representatives.join(" "))
420    }
421
422    /// Checks whether every coefficient is mapped into `(-⌈modulus/2⌉, ⌊modulus/2⌋]`
423    /// for an odd and an even modulus, i.e. `modulus/2` is mapped to the positive representative.
424    #[test]
425    fn full_range() {
426        for modulus in [10, 11] {
427            let (coefficients, representatives) = coefficients_and_representatives(modulus);
428            let poly_zq =
429                PolyOverZq::from_str(&format!("{modulus}  {coefficients} mod {modulus}")).unwrap();
430
431            let poly_z = poly_zq.get_representative_least_absolute_residue();
432
433            let cmp_poly = PolyOverZ::from_str(&format!("{modulus}  {representatives}")).unwrap();
434            assert_eq!(cmp_poly, poly_z);
435        }
436    }
437}
438
439#[cfg(test)]
440mod test_get_representative_least_nonnegative_residue {
441    use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};
442    use std::str::FromStr;
443
444    /// Ensure that the getter works for large entries.
445    #[test]
446    fn large_positive() {
447        let large_prime = u64::MAX - 58;
448        let poly_zq = PolyOverZq::from_str(&format!("4  -1 0 0 1 mod {large_prime}")).unwrap();
449
450        let poly_z = poly_zq.get_representative_least_nonnegative_residue();
451
452        let cmp_poly = PolyOverZ::from_str(&format!("4  {} 0 0 1", u64::MAX - 59)).unwrap();
453        assert_eq!(cmp_poly, poly_z);
454    }
455}