Skip to main content

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