qfall_math/integer_mod_q/polynomial_ring_zq/
from.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 create a [`PolynomialRingZq`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::PolynomialRingZq;
14use crate::{
15    error::{MathError, StringConversionError},
16    integer::PolyOverZ,
17    integer_mod_q::{ModulusPolynomialRingZq, NTTPolynomialRingZq, PolyOverZq},
18    macros::for_others::implement_for_owned,
19};
20use std::str::FromStr;
21
22impl From<NTTPolynomialRingZq> for PolynomialRingZq {
23    /// Creates a polynomial from [`NTTPolynomialRingZq`] generated with respect to the
24    /// [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq) as part of
25    /// [`ModulusPolynomialRingZq`].
26    ///
27    /// Parameters:
28    /// - `ntt`: the NTT representation of the polynomial.
29    /// - `modulus`: the modulus that is applied to the polynomial ring element.
30    ///
31    /// Returns a new [`PolynomialRingZq`] with the specified [`ModulusPolynomialRingZq`] and
32    /// values as defined in `ntt`.
33    ///
34    /// # Examples
35    /// ```
36    /// use qfall_math::integer_mod_q::{PolynomialRingZq, PolyOverZq, ModulusPolynomialRingZq, NTTPolynomialRingZq};
37    /// use qfall_math::traits::SetCoefficient;
38    ///
39    /// let n = 4;
40    /// let modulus = 7681;
41    ///
42    /// let mut mod_poly = PolyOverZq::from(modulus);
43    /// mod_poly.set_coeff(0, 1).unwrap();
44    /// mod_poly.set_coeff(n, 1).unwrap();
45    ///
46    /// let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
47    /// polynomial_modulus.set_ntt_unchecked(1925);
48    ///
49    /// let ntt = NTTPolynomialRingZq::sample_uniform(&polynomial_modulus);
50    ///
51    /// let res = PolynomialRingZq::from(ntt);
52    /// ```
53    ///
54    /// # Panics ...
55    /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq) in `modulus`
56    ///   is not set.
57    fn from(ntt: NTTPolynomialRingZq) -> Self {
58        ntt.inv_ntt()
59    }
60}
61
62impl From<&ModulusPolynomialRingZq> for PolynomialRingZq {
63    /// Creates a zero polynomial with a given [`ModulusPolynomialRingZq`].
64    ///
65    /// Parameters:
66    /// - `modulus`: the modulus that is applied to the polynomial ring element.
67    ///
68    /// Returns a new constant [`PolynomialRingZq`] with the specified [`ModulusPolynomialRingZq`].
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer_mod_q::PolynomialRingZq;
73    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
74    /// use qfall_math::integer_mod_q::PolyOverZq;
75    /// use std::str::FromStr;
76    ///
77    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
78    /// let poly = PolyOverZq::from_str("4  -1 0 1 1 mod 17").unwrap();
79    /// let poly_ring = PolynomialRingZq::from((poly, &modulus));
80    /// ```
81    ///
82    /// # Panics ...
83    /// - if the moduli mismatch.
84    fn from(modulus: &ModulusPolynomialRingZq) -> Self {
85        let modulus = modulus.into();
86
87        Self {
88            poly: PolyOverZ::default(),
89            modulus,
90        }
91    }
92}
93
94implement_for_owned!(ModulusPolynomialRingZq, PolynomialRingZq, From);
95
96impl<Poly: Into<PolyOverZ>, Mod: Into<ModulusPolynomialRingZq>> From<(Poly, Mod)>
97    for PolynomialRingZq
98{
99    /// Creates a new polynomial ring element of type [`PolynomialRingZq`].
100    ///
101    /// Parameters:
102    /// - `poly`: the coefficients of the polynomial.
103    /// - `modulus`: the modulus that is applied to the polynomial ring element.
104    ///
105    /// Returns a new element inside the polynomial ring.
106    ///
107    /// # Examples
108    /// ```
109    /// use qfall_math::integer_mod_q::PolynomialRingZq;
110    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
111    /// use qfall_math::integer::PolyOverZ;
112    /// use std::str::FromStr;
113    ///
114    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
115    /// let poly = PolyOverZ::from_str("4  -1 0 1 1").unwrap();
116    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
117    /// ```
118    fn from((poly, modulus): (Poly, Mod)) -> Self {
119        let mut out = Self {
120            poly: poly.into(),
121            modulus: modulus.into(),
122        };
123        out.reduce();
124        out
125    }
126}
127
128impl<Mod: Into<ModulusPolynomialRingZq>> From<(&PolyOverZq, Mod)> for PolynomialRingZq {
129    /// Creates a new polynomial ring element of type [`PolynomialRingZq`].
130    ///
131    /// Parameters:
132    /// - `poly`: the coefficients of the polynomial.
133    /// - `modulus`: the modulus that is applied to the polynomial ring element.
134    ///
135    /// Returns a new element inside the polynomial ring, if the moduli of the
136    /// polynomial and the modulus match.
137    ///
138    /// # Examples
139    /// ```
140    /// use qfall_math::integer_mod_q::PolynomialRingZq;
141    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
142    /// use qfall_math::integer_mod_q::PolyOverZq;
143    /// use std::str::FromStr;
144    ///
145    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
146    /// let poly = PolyOverZq::from_str("4  -1 0 1 1 mod 17").unwrap();
147    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
148    /// ```
149    ///
150    /// # Panics ...
151    /// - if the moduli mismatch.
152    fn from((poly, modulus): (&PolyOverZq, Mod)) -> Self {
153        let modulus = modulus.into();
154
155        assert_eq!(
156            poly.modulus,
157            modulus.get_q(),
158            "The moduli of the polynomial and the modulus mismatch."
159        );
160
161        let mut out = Self {
162            poly: poly.get_representative_least_nonnegative_residue(),
163            modulus,
164        };
165        out.reduce();
166        out
167    }
168}
169
170impl<Mod: Into<ModulusPolynomialRingZq>> From<(PolyOverZq, Mod)> for PolynomialRingZq {
171    /// Creates a new polynomial ring element of type [`PolynomialRingZq`].
172    ///
173    /// Parameters:
174    /// - `poly`: the coefficients of the polynomial.
175    /// - `modulus`: the modulus that is applied to the polynomial ring element.
176    ///
177    /// Returns a new element inside the polynomial ring, if the moduli of the
178    /// polynomial and the modulus match.
179    ///
180    /// # Examples
181    /// ```
182    /// use qfall_math::integer_mod_q::PolynomialRingZq;
183    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
184    /// use qfall_math::integer_mod_q::PolyOverZq;
185    /// use std::str::FromStr;
186    ///
187    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
188    /// let poly = PolyOverZq::from_str("4  -1 0 1 1 mod 17").unwrap();
189    /// let poly_ring = PolynomialRingZq::from((poly, &modulus));
190    /// ```
191    ///
192    /// # Panics ...
193    /// - if the moduli mismatch.
194    fn from((poly, modulus): (PolyOverZq, Mod)) -> Self {
195        let modulus = modulus.into();
196
197        assert_eq!(
198            poly.modulus,
199            modulus.get_q(),
200            "The moduli of the polynomial and the modulus mismatch."
201        );
202
203        let mut out = Self {
204            poly: poly.get_representative_least_nonnegative_residue(),
205            modulus,
206        };
207        out.reduce();
208        out
209    }
210}
211
212impl FromStr for PolynomialRingZq {
213    type Err = MathError;
214
215    /// Creates a polynomial ring element of type [`PolynomialRingZq`].
216    ///
217    /// **Warning**: If the polynomials start with a correctly formatted
218    /// [`PolyOverZ`] object, the rest of the string
219    /// until the `"/"` (for the first polynomial)  or `"mod"` (for the second polynomial)
220    /// is ignored. This means that the input string `"4  0 1 2 3 / 2  1 1 mod 13"`
221    /// is the same as `"4  0 1 2 3 4 5 6 7 / 2  1 1 mod 13"`.
222    ///
223    /// Parameters:
224    /// - `s`: the polynomial ring element of form:
225    ///   `"[#number of coefficients of element]⌴⌴[0th coefficient]⌴
226    ///     [1st coefficient]⌴...⌴/⌴[#number of coefficients of polynomial modulus]⌴⌴
227    ///     [0th coefficient]⌴[1st coefficient]⌴...⌴mod⌴[q]"`.
228    ///
229    /// Note that the `[#number of coefficients]` and `[0th coefficient]`
230    /// are divided by two spaces and the strings for the polynomials are trimmed,
231    /// i.e. all whitespaces around the polynomials and the modulus are ignored.
232    ///
233    /// Returns a [`PolynomialRingZq`] or an error if the provided string was not
234    /// formatted correctly, the numbers of coefficients were smaller than the numbers
235    /// provided at the start of the provided string, or the modulus was smaller than `2`.
236    ///
237    /// # Examples
238    /// ```
239    /// use qfall_math::integer_mod_q::PolynomialRingZq;
240    /// use std::str::FromStr;
241    ///
242    /// let poly = PolynomialRingZq::from_str("4  -1 0 1 1 / 4  0 1 -2 3 mod 42").unwrap();
243    /// ```
244    /// # Errors and Failures
245    /// - Returns a [`MathError`] of type
246    ///   [`StringConversionError`](MathError::StringConversionError)
247    ///     - if the provided first half of the string was not formatted correctly to
248    ///       create a [`PolyOverZ`],
249    ///     - if the provided second half of the
250    ///       string was not formatted correctly to create a [`ModulusPolynomialRingZq`],
251    ///     - if the numbers of coefficients were smaller than the numbers provided
252    ///       at the start of the provided string,
253    ///     - if the provided values did not contain two whitespaces, or
254    ///     - if the delimiter `/` and `mod` could not be found.
255    /// - Returns a [`MathError`] of type
256    ///   [`InvalidModulus`](MathError::InvalidModulus)
257    ///   if the integer modulus `q` is smaller than `2`.
258    fn from_str(s: &str) -> Result<Self, Self::Err> {
259        let (poly_s, modulus) = match s.split_once("/") {
260            Some((poly_s, modulus)) => (poly_s, modulus),
261            None => {
262                return Err(StringConversionError::InvalidStringToPolyRingZqInput(
263                    s.to_owned(),
264                ))?;
265            }
266        };
267
268        let poly_over_z = PolyOverZ::from_str(poly_s)?;
269        let modulus = ModulusPolynomialRingZq::from_str(modulus)?;
270
271        Ok(Self::from((&poly_over_z, &modulus)))
272    }
273}
274
275#[cfg(test)]
276mod test_from_ntt_modulus_polynomial_ring_zq {
277    use crate::{
278        integer::{PolyOverZ, Z},
279        integer_mod_q::{ModulusPolynomialRingZq, NTTPolynomialRingZq, PolynomialRingZq},
280    };
281    use std::str::FromStr;
282
283    /// Ensures that `inv_ntt` works properly and that we can make a round trip and get to the same ntt representation.
284    #[test]
285    fn round_trip() {
286        let mut mod_poly = ModulusPolynomialRingZq::from_str("5  1 0 0 0 1 mod 257").unwrap();
287        mod_poly.set_ntt_unchecked(64);
288
289        let poly = PolyOverZ::from_str("4  103 182 146 116").unwrap();
290        let poly_ring_zq = PolynomialRingZq::from((poly, &mod_poly));
291        let cmp_ntt = NTTPolynomialRingZq {
292            poly: vec![Z::from(113), Z::from(54), Z::from(47), Z::from(198)],
293            modulus: mod_poly.clone(),
294        };
295
296        let ntt = NTTPolynomialRingZq::from(&poly_ring_zq);
297
298        assert_eq!(ntt, cmp_ntt);
299
300        let res_poly_ring_zq = PolynomialRingZq::from(ntt);
301
302        assert_eq!(res_poly_ring_zq, poly_ring_zq);
303    }
304}
305
306#[cfg(test)]
307mod test_from_modulus_polynomial_ring_zq {
308    use crate::{
309        integer::PolyOverZ,
310        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
311    };
312    use std::str::FromStr;
313
314    /// Ensure that the default value is set correctly and that the modulus is correct
315    #[test]
316    fn is_reduced_large() {
317        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
318
319        let poly_ring = PolynomialRingZq::from(&modulus);
320
321        assert_eq!(PolyOverZ::default(), poly_ring.poly);
322        assert_eq!(modulus, poly_ring.modulus);
323    }
324
325    /// Ensures that the function is still available for all values implementing
326    /// `Into<ModulusPolynomialRingZq>`.
327    #[test]
328    fn availability() {
329        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
330
331        let _ = PolynomialRingZq::from(&modulus);
332        let _ = PolynomialRingZq::from(modulus);
333    }
334}
335
336#[cfg(test)]
337mod test_from_poly_over_z_modulus_polynomial_ring_zq {
338    use crate::{
339        integer::{PolyOverZ, Z},
340        integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq},
341    };
342    use std::str::FromStr;
343
344    const LARGE_PRIME: u64 = u64::MAX - 58;
345
346    /// Ensure that the modulus is applied with a large prime and large coefficients
347    #[test]
348    fn is_reduced_large() {
349        let modulus =
350            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
351
352        let poly =
353            PolyOverZ::from_str(&format!("4  {} {} 1 1", LARGE_PRIME + 2, u64::MAX)).unwrap();
354        let poly_ring = PolynomialRingZq::from((&poly, &modulus));
355
356        let cmp_poly = PolyOverZ::from_str("3  1 58 1").unwrap();
357        let cmp_poly_ring = PolynomialRingZq::from((&cmp_poly, &modulus));
358
359        assert_eq!(poly_ring, cmp_poly_ring);
360    }
361
362    /// Ensure that two ring elements that are instantiated the same way are equal
363    #[test]
364    fn same_instantiation() {
365        let modulus =
366            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
367        let poly =
368            PolyOverZ::from_str(&format!("4  {} {} 1 1", LARGE_PRIME + 2, u64::MAX)).unwrap();
369
370        let poly_ring_1 = PolynomialRingZq::from((&poly, &modulus));
371        let poly_ring_2 = PolynomialRingZq::from((&poly, &modulus));
372
373        assert_eq!(poly_ring_1, poly_ring_2);
374    }
375
376    /// Ensures that the function is still available for all values implementing
377    /// `Into<ModulusPolynomialRingZq>`.
378    #[test]
379    fn availability() {
380        let z = Z::from(2);
381        let q = Modulus::from(17);
382        let poly = PolyOverZ::from(2);
383        let poly_mod = PolyOverZq::from_str("2  1 1 mod 17").unwrap();
384        let modulus = ModulusPolynomialRingZq::from(&poly_mod);
385
386        let _ = PolynomialRingZq::from((&poly, &poly_mod));
387        let _ = PolynomialRingZq::from((&poly, poly_mod.clone()));
388        let _ = PolynomialRingZq::from((poly.clone(), &poly_mod));
389        let _ = PolynomialRingZq::from((poly.clone(), poly_mod));
390
391        let _ = PolynomialRingZq::from((0_i8, &modulus));
392        let _ = PolynomialRingZq::from((0_i16, &modulus));
393        let _ = PolynomialRingZq::from((0_i32, &modulus));
394        let _ = PolynomialRingZq::from((0_i64, &modulus));
395        let _ = PolynomialRingZq::from((0_u8, &modulus));
396        let _ = PolynomialRingZq::from((0_u16, &modulus));
397        let _ = PolynomialRingZq::from((0_u32, &modulus));
398        let _ = PolynomialRingZq::from((0_u64, &modulus));
399        let _ = PolynomialRingZq::from((&z, &modulus));
400        let _ = PolynomialRingZq::from((z, &modulus));
401        let _ = PolynomialRingZq::from((&q, &modulus));
402        let _ = PolynomialRingZq::from((q, &modulus));
403
404        let _ = PolynomialRingZq::from((poly.clone(), &modulus));
405        let _ = PolynomialRingZq::from((poly, modulus));
406    }
407}
408
409#[cfg(test)]
410mod test_from_poly_over_zq_modulus_polynomial_ring_zq {
411    use crate::{
412        integer::PolyOverZ,
413        integer_mod_q::{ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq},
414    };
415    use std::str::FromStr;
416
417    const LARGE_PRIME: u64 = u64::MAX - 58;
418
419    /// Ensure that the modulus is applied with a large prime and large coefficients
420    #[test]
421    fn is_reduced_large() {
422        let modulus =
423            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
424
425        let poly = PolyOverZq::from_str(&format!(
426            "4  {} {} 1 1 mod {}",
427            LARGE_PRIME + 2,
428            u64::MAX,
429            LARGE_PRIME
430        ))
431        .unwrap();
432        let poly_ring = PolynomialRingZq::from((&poly, &modulus));
433
434        let cmp_poly = PolyOverZ::from_str("3  1 58 1").unwrap();
435        let cmp_poly_ring = PolynomialRingZq::from((&cmp_poly, &modulus));
436
437        assert_eq!(poly_ring, cmp_poly_ring);
438    }
439
440    /// Ensure that two ring elements that are instantiated the same way are equal
441    #[test]
442    fn same_instantiation() {
443        let modulus =
444            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
445        let poly = PolyOverZq::from_str(&format!(
446            "4  {} {} 1 1 mod {}",
447            LARGE_PRIME + 2,
448            u64::MAX,
449            LARGE_PRIME
450        ))
451        .unwrap();
452
453        let poly_ring_1 = PolynomialRingZq::from((&poly, &modulus));
454        let poly_ring_2 = PolynomialRingZq::from((&poly, &modulus));
455
456        assert_eq!(poly_ring_1, poly_ring_2);
457    }
458
459    /// Ensures that the function is still available for all values implementing
460    /// `Into<ModulusPolynomialRingZq>`.
461    #[test]
462    fn availability() {
463        let poly_mod = PolyOverZq::from_str("2  1 1 mod 17").unwrap();
464        let modulus = ModulusPolynomialRingZq::from(&poly_mod);
465
466        let _ = PolynomialRingZq::from((&poly_mod, &poly_mod));
467        let _ = PolynomialRingZq::from((&poly_mod, poly_mod.clone()));
468        let _ = PolynomialRingZq::from((poly_mod.clone(), &poly_mod));
469        let _ = PolynomialRingZq::from((poly_mod.clone(), poly_mod.clone()));
470
471        let _ = PolynomialRingZq::from((&poly_mod, &modulus));
472        let _ = PolynomialRingZq::from((&poly_mod, modulus.clone()));
473        let _ = PolynomialRingZq::from((poly_mod.clone(), &modulus));
474        let _ = PolynomialRingZq::from((poly_mod, modulus));
475    }
476
477    /// Ensure that the function panics if the moduli mismatch.
478    #[test]
479    #[should_panic]
480    fn mismatiching_modulus_error_borrowed() {
481        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
482        let poly = PolyOverZq::from_str("4  -1 0 1 1 mod 13").unwrap();
483
484        let _ = PolynomialRingZq::from((&poly, &modulus));
485    }
486
487    /// Ensure that the function panics if the moduli mismatch.
488    #[test]
489    #[should_panic]
490    fn mismatiching_modulus_error_owned() {
491        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
492        let poly = PolyOverZq::from_str("4  -1 0 1 1 mod 13").unwrap();
493
494        let _ = PolynomialRingZq::from((poly, &modulus));
495    }
496}
497
498#[cfg(test)]
499mod test_from_str {
500    use super::PolynomialRingZq;
501    use std::str::FromStr;
502
503    /// tests whether a falsely formatted string (integer modulus is 0) returns an
504    /// error
505    #[test]
506    fn modulus_zero_throws_error() {
507        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / 4  0 1 -2 3 mod 0").is_err());
508    }
509
510    /// tests whether a false string (negative modulus) returns an error
511    #[test]
512    fn false_sign() {
513        assert!(PolynomialRingZq::from_str("4  0 1 -2 3 mod -42").is_err());
514    }
515
516    /// tests whether a falsely formatted string (wrong whitespaces) returns an
517    /// error
518    #[test]
519    fn whitespaces_in_modulus() {
520        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / 4  0 1 -2 3 mod 4 2").is_err());
521    }
522
523    /// tests whether a falsely formatted string (wrong symbols) returns an error
524    #[test]
525    fn false_format_symbols() {
526        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / 1  1 mod ba").is_err());
527        assert!(PolynomialRingZq::from_str("4  -1 0 1a 1 / 1  1 mod 42").is_err());
528        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / b  1 mod 42").is_err());
529    }
530
531    /// tests whether a falsely formatted string (missing double-space) returns
532    /// an error
533    #[test]
534    fn false_format() {
535        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / 4 0 1 -2 3 mod 42").is_err());
536        assert!(PolynomialRingZq::from_str("4 -1 0 1 1 / 4  0 1 -2 3 mod 42").is_err());
537    }
538
539    /// tests whether a falsely formatted string (wrong number of total
540    /// coefficients) returns an error
541    #[test]
542    fn false_number_of_coefficient() {
543        assert!(PolynomialRingZq::from_str("4  -1 0 1 1 / 5  0 1 -2 3 mod 42").is_err());
544        assert!(PolynomialRingZq::from_str("5  -1 0 1 1 / 4  0 1 -2 3 mod 42").is_err());
545    }
546
547    /// tests whether a falsely formatted string (too many whitespaces) returns
548    /// an error
549    #[test]
550    fn too_many_whitespaces() {
551        assert!(PolynomialRingZq::from_str("4  -1  0  1  1 / 4  0  1  -2  3 mod 42").is_err());
552    }
553
554    /// Ensure that the input works with strings that have to be trimmed
555    #[test]
556    fn trim_input() {
557        let poly = PolynomialRingZq::from_str(
558            "        4  -1 0 1 1     /            4  1 2 3 -4                  mod              17                     ",
559        );
560        assert!(poly.is_ok());
561        assert_eq!(
562            PolynomialRingZq::from_str("4  -1 0 1 1 / 4  1 2 3 -4 mod 17").unwrap(),
563            poly.unwrap()
564        );
565    }
566
567    /// Ensure that a string resulting from to_string, can be used in from_str
568    #[test]
569    fn roundtrip() {
570        let poly = PolynomialRingZq::from_str("2  1 1 / 4  1 1 -2 3 mod 42").unwrap();
571        let poly2 = PolynomialRingZq::from_str(&poly.to_string()).unwrap();
572        assert_eq!(poly, poly2);
573    }
574}