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}