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}