qfall_math/integer_mod_q/polynomial_ring_zq/get.rs
1// Copyright 2023 Marcel Luca Schmidt, 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 content of a [`PolynomialRingZq].
10
11use super::PolynomialRingZq;
12use crate::{
13 integer::{PolyOverZ, Z},
14 integer_mod_q::{ModulusPolynomialRingZq, Zq},
15 traits::{GetCoefficient, SetCoefficient},
16};
17use flint3_sys::{fmpz_poly_degree, fmpz_poly_get_coeff_fmpz};
18
19impl GetCoefficient<Zq> for PolynomialRingZq {
20 /// Returns the coefficient of a [`PolynomialRingZq`] 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::{PolynomialRingZq, Zq};
34 /// use std::str::FromStr;
35 ///
36 /// let poly_ring = PolynomialRingZq::from_str("3 0 1 1 / 4 1 0 0 1 mod 17").unwrap();
37 ///
38 /// let coeff_0: Zq = poly_ring.get_coeff(0).unwrap();
39 /// let coeff_1: Zq = unsafe{ poly_ring.get_coeff_unchecked(1) };
40 /// let coeff_3: Zq = poly_ring.get_coeff(3).unwrap();
41 ///
42 /// assert_eq!(Zq::from((0, 17)), coeff_0);
43 /// assert_eq!(Zq::from((1, 17)), coeff_1);
44 /// assert_eq!(Zq::from((0, 17)), coeff_3);
45 /// ```
46 ///
47 /// # Safety
48 /// To use this function safely, make sure that the selected index
49 /// is greater or equal than `0`.
50 unsafe fn get_coeff_unchecked(&self, index: i64) -> Zq {
51 let mut out_z = Z::default();
52 unsafe { fmpz_poly_get_coeff_fmpz(&mut out_z.value, &self.poly.poly, index) }
53 Zq::from((out_z, &self.modulus.get_q()))
54 }
55}
56
57impl GetCoefficient<Z> for PolynomialRingZq {
58 /// Returns the coefficient of a [`PolynomialRingZq`] 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::{PolyOverZ, Z};
72 /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
73 /// use std::str::FromStr;
74 ///
75 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
76 /// let poly = PolyOverZ::from_str("3 0 1 1").unwrap();
77 /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
78 ///
79 /// let coeff_0: Z = poly_ring.get_coeff(0).unwrap();
80 /// let coeff_1: Z = unsafe{ poly_ring.get_coeff_unchecked(1) };
81 /// let coeff_3: Z = poly_ring.get_coeff(3).unwrap();
82 ///
83 /// assert_eq!(Z::ZERO, coeff_0);
84 /// assert_eq!(Z::ONE, coeff_1);
85 /// assert_eq!(Z::ZERO, coeff_3);
86 /// ```
87 ///
88 /// # Safety
89 /// To use this function safely, make sure that the selected index
90 /// is greater or equal than `0`.
91 unsafe fn get_coeff_unchecked(&self, index: i64) -> Z {
92 let mut out = Z::default();
93 unsafe { fmpz_poly_get_coeff_fmpz(&mut out.value, &self.poly.poly, index) }
94 out
95 }
96}
97
98impl PolynomialRingZq {
99 /// Returns the modulus object of the [`PolynomialRingZq`] element.
100 ///
101 /// # Examples
102 /// ```
103 /// use qfall_math::integer::PolyOverZ;
104 /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
105 /// use std::str::FromStr;
106 ///
107 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
108 /// let poly = PolyOverZ::from_str("4 -1 0 1 1").unwrap();
109 /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
110 ///
111 /// let poly_ring_mod = poly_ring.get_mod();
112 ///
113 /// assert_eq!(modulus, poly_ring_mod);
114 /// ```
115 pub fn get_mod(&self) -> ModulusPolynomialRingZq {
116 self.modulus.clone()
117 }
118
119 /// Returns a representative polynomial of the [`PolynomialRingZq`] element with coefficients centered around `0`.
120 ///
121 /// The output [`PolyOverZ`] has coefficients in the range of `[-modulus/2, modulus/2]`.
122 /// For even moduli, the positive representative is chosen for the element `modulus / 2`.
123 /// Use [`PolynomialRingZq::get_representative_least_nonnegative_residue`] if they should be
124 /// in the range `[0, modulus)`.
125 ///
126 /// # Examples
127 /// ```
128 /// use qfall_math::integer_mod_q::PolynomialRingZq;
129 /// use qfall_math::integer::PolyOverZ;
130 /// use std::str::FromStr;
131 ///
132 /// let value = PolynomialRingZq::from_str("2 10 11 / 4 1 0 0 1 mod 21").unwrap();
133 ///
134 /// let least_abs_residue = value.get_representative_least_absolute_residue();
135 ///
136 /// assert_eq!(PolyOverZ::from_str("2 10 -10").unwrap(), least_abs_residue);
137 /// ```
138 pub fn get_representative_least_absolute_residue(&self) -> PolyOverZ {
139 let mut out = PolyOverZ::default();
140 for i in 0..=self.get_degree() {
141 let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
142 let lar_coeff = coeff.get_representative_least_absolute_residue();
143 unsafe { out.set_coeff_unchecked(i, lar_coeff) };
144 }
145 out
146 }
147
148 /// Returns a representative polynomial of the [`PolynomialRingZq`] element.
149 ///
150 /// The representation of the coefficients is in the range `[0, modulus)` and
151 /// the representation of the polynomial is in the range `[0, modulus_polynomial)`.
152 /// Use [`PolynomialRingZq::get_representative_least_absolute_residue`] if they should be
153 /// in the range `[-modulus/2, modulus/2]`.
154 ///
155 /// # Examples
156 /// ```
157 /// use qfall_math::integer::PolyOverZ;
158 /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
159 /// use std::str::FromStr;
160 ///
161 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
162 /// let poly = PolyOverZ::from_str("4 -1 0 1 1").unwrap();
163 /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
164 ///
165 /// let poly_z = poly_ring.get_representative_least_nonnegative_residue();
166 ///
167 /// let cmp_poly = PolyOverZ::from_str("3 15 0 1").unwrap();
168 /// assert_eq!(cmp_poly, poly_z);
169 /// ```
170 pub fn get_representative_least_nonnegative_residue(&self) -> PolyOverZ {
171 self.poly.clone()
172 }
173
174 /// Returns the degree of a [`PolynomialRingZq`] as a [`i64`].
175 /// The zero polynomial has degree `-1`.
176 ///
177 /// # Examples
178 /// ```
179 /// use qfall_math::integer::PolyOverZ;
180 /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
181 /// use std::str::FromStr;
182 ///
183 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
184 /// let poly = PolyOverZ::from_str("3 0 1 1").unwrap();
185 /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
186 ///
187 /// let degree = poly_ring.get_degree();
188 ///
189 /// assert_eq!(2, degree);
190 /// ```
191 pub fn get_degree(&self) -> i64 {
192 unsafe { fmpz_poly_degree(&self.poly.poly) }
193 }
194}
195
196#[cfg(test)]
197mod test_get_coeff {
198 use crate::{
199 integer::{PolyOverZ, Z},
200 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq, Zq},
201 traits::GetCoefficient,
202 };
203 use std::str::FromStr;
204
205 /// Ensure that 0 is returned if the provided index is not yet set.
206 #[test]
207 fn index_out_of_range() {
208 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
209 let poly = PolyOverZ::from_str("3 1 1 1").unwrap();
210 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
211
212 let zero_coeff_z: Z = poly_ring.get_coeff(3).unwrap();
213 let zero_coeff_zq = poly_ring.get_coeff(3).unwrap();
214
215 assert_eq!(0, zero_coeff_z);
216 assert_eq!(Zq::from((0, 17)), zero_coeff_zq);
217 }
218
219 /// Tests if coefficients are returned correctly.
220 #[test]
221 fn coeff_correct() {
222 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
223 let poly = PolyOverZ::from_str("3 1 0 3").unwrap();
224 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
225
226 let coeff_z: Z = poly_ring.get_coeff(2).unwrap();
227 let coeff_zq = poly_ring.get_coeff(2).unwrap();
228
229 assert_eq!(3, coeff_z);
230 assert_eq!(Zq::from((3, 17)), coeff_zq);
231 }
232
233 /// Tests if large coefficients are returned correctly.
234 #[test]
235 fn large_coeff() {
236 let modulus =
237 ModulusPolynomialRingZq::from_str(&format!("5 1 0 4 1 1 mod {}", u64::MAX)).unwrap();
238 let poly = PolyOverZ::from_str(&format!("3 0 {} 1", i64::MAX)).unwrap();
239 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
240
241 let coefficient_1: Z = poly_ring.get_coeff(1).unwrap();
242 let coefficient_2: Zq = poly_ring.get_coeff(1).unwrap();
243
244 assert_eq!(i64::MAX, coefficient_1);
245 assert_eq!(Zq::from((i64::MAX, u64::MAX)), coefficient_2);
246 }
247
248 /// Tests if negative index yields an error in get_coeff with [`Z`].
249 #[test]
250 #[should_panic]
251 fn negative_index_error_z() {
252 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
253 let poly = PolyOverZ::from_str("3 1 0 3").unwrap();
254 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
255
256 let _: Z = poly_ring.get_coeff(-1).unwrap();
257 }
258
259 /// Tests if negative index yields an error in get_coeff with [`Zq`].
260 #[test]
261 #[should_panic]
262 fn negative_index_error_zq() {
263 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
264 let poly = PolyOverZ::from_str("3 1 0 3").unwrap();
265 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
266
267 let _: Zq = poly_ring.get_coeff(-1).unwrap();
268 }
269}
270
271#[cfg(test)]
272mod test_get_mod {
273 use crate::{
274 integer::PolyOverZ,
275 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
276 };
277 use std::str::FromStr;
278
279 /// Ensure that the large modulus polynomial is returned correctly.
280 #[test]
281 fn large_positive() {
282 let modulus =
283 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX - 58))
284 .unwrap();
285 let poly = PolyOverZ::from_str("4 -1 0 1 1").unwrap();
286 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
287
288 assert_eq!(modulus, poly_ring.get_mod());
289 }
290}
291
292#[cfg(test)]
293mod test_get_representative_least_absolute_residue {
294 use crate::{integer::PolyOverZ, integer_mod_q::PolynomialRingZq};
295 use std::str::FromStr;
296
297 /// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
298 #[test]
299 fn large_numbers() {
300 let poly_ring = PolynomialRingZq::from_str(&format!(
301 "2 {} {} / 4 1 0 0 1 mod {}",
302 i64::MAX,
303 u64::MAX - 1,
304 u64::MAX
305 ))
306 .unwrap();
307
308 let poly_z = poly_ring.get_representative_least_absolute_residue();
309
310 let cmp_poly = PolyOverZ::from_str(&format!("2 {} -1", i64::MAX)).unwrap();
311 assert_eq!(cmp_poly, poly_z);
312 }
313
314 /// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
315 #[test]
316 fn special_numbers() {
317 let poly_ring = PolynomialRingZq::from_str("3 10 0 11 / 4 1 0 0 1 mod 21").unwrap();
318
319 let poly_z = poly_ring.get_representative_least_absolute_residue();
320
321 let cmp_poly = PolyOverZ::from_str("3 10 0 -10").unwrap();
322 assert_eq!(cmp_poly, poly_z);
323 }
324}
325
326#[cfg(test)]
327mod test_get_representative_least_nonnegative_residue {
328 use crate::{
329 integer::PolyOverZ,
330 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
331 };
332 use std::str::FromStr;
333
334 /// Ensure that the getter works for large entries.
335 #[test]
336 fn large_positive() {
337 let large_prime = u64::MAX - 58;
338 let modulus =
339 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {large_prime}")).unwrap();
340 let poly = PolyOverZ::from_str("4 -1 0 1 1").unwrap();
341 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
342
343 let poly_z = poly_ring.get_representative_least_nonnegative_residue();
344
345 let cmp_poly = PolyOverZ::from_str(&format!("3 {} 0 1", u64::MAX - 60)).unwrap();
346 assert_eq!(cmp_poly, poly_z);
347 }
348}
349
350#[cfg(test)]
351mod test_get_degree {
352 use crate::{
353 integer::PolyOverZ,
354 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
355 };
356 use std::str::FromStr;
357
358 /// Ensure that degree is working.
359 #[test]
360 fn degree() {
361 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
362 let poly = PolyOverZ::from_str("3 0 1 1").unwrap();
363 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
364
365 let deg = poly_ring.get_degree();
366
367 assert_eq!(2, deg);
368 }
369
370 /// Ensure that degree is working for constant polynomials.
371 #[test]
372 fn degree_constant() {
373 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
374 let poly = PolyOverZ::from(1);
375 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
376
377 let deg = poly_ring.get_degree();
378
379 assert_eq!(0, deg);
380 }
381
382 /// Ensure that degree is working for polynomials with leading 0 coefficients.
383 #[test]
384 fn degree_leading_zeros() {
385 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
386 let poly = PolyOverZ::from_str("3 1 0 0").unwrap();
387 let poly_ring = PolynomialRingZq::from((&poly, &modulus));
388
389 let deg = poly_ring.get_degree();
390
391 assert_eq!(0, deg);
392 }
393}