Skip to main content

qfall_math/integer_mod_q/modulus_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 [`ModulusPolynomialRingZq`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::ModulusPolynomialRingZq;
14use crate::{
15    error::MathError,
16    integer::{PolyOverZ, Z},
17    integer_mod_q::{Modulus, PolyOverZq},
18    macros::for_others::implement_for_owned,
19    traits::GetCoefficient,
20};
21use std::{rc::Rc, str::FromStr};
22
23impl<Mod: Into<Modulus>> From<(&PolyOverZ, Mod)> for ModulusPolynomialRingZq {
24    /// Creates a [`ModulusPolynomialRingZq`] from a [`PolyOverZ`] and a value that implements [`Into<Modulus>`].
25    /// It requires that the leading coefficient is `1`.
26    ///
27    /// Parameters:
28    /// - `poly`: the coefficients of the polynomial.
29    /// - `modulus`: the modulus by which each entry is reduced.
30    ///
31    /// Returns a new [`ModulusPolynomialRingZq`] with the coefficients from the
32    /// [`PolyOverZ`] instance under the specified [`Modulus`] value.
33    ///
34    /// # Examples
35    /// ```
36    /// use qfall_math::integer_mod_q::{ModulusPolynomialRingZq, Modulus};
37    /// use qfall_math::integer::PolyOverZ;
38    /// use std::str::FromStr;
39    ///
40    /// let poly = PolyOverZ::from_str("4  0 1 102 1").unwrap();
41    ///
42    /// let mod_poly = ModulusPolynomialRingZq::from((&poly, 100));
43    ///
44    /// let poly_cmp = ModulusPolynomialRingZq::from_str("4  0 1 2 1 mod 100").unwrap();
45    /// assert_eq!(poly_cmp, mod_poly);
46    /// ```
47    ///
48    /// # Panics ...
49    /// - if `modulus` is smaller than `2`, or
50    /// - if the degree of the polynomial is smaller than `1`.
51    fn from((poly, modulus): (&PolyOverZ, Mod)) -> Self {
52        let poly_zq = PolyOverZq::from((poly, modulus));
53
54        check_poly_mod(&poly_zq).unwrap();
55
56        Self::from(poly_zq)
57    }
58}
59
60impl<Mod: Into<Modulus>> From<(PolyOverZ, Mod)> for ModulusPolynomialRingZq {
61    /// Creates a [`ModulusPolynomialRingZq`] from a [`PolyOverZ`] and a value that implements [`Into<Modulus>`].
62    /// It requires that the leading coefficient is `1`.
63    ///
64    /// Parameters:
65    /// - `poly`: the coefficients of the polynomial.
66    /// - `modulus`: the modulus by which each entry is reduced.
67    ///
68    /// Returns a new [`ModulusPolynomialRingZq`] with the coefficients from the
69    /// [`PolyOverZ`] instance under the specified [`Modulus`] value.
70    ///
71    /// # Examples
72    /// ```
73    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
74    /// use qfall_math::integer::PolyOverZ;
75    /// use std::str::FromStr;
76    ///
77    /// let poly = PolyOverZ::from_str("4  0 1 102 1").unwrap();
78    ///
79    /// let mod_poly = ModulusPolynomialRingZq::from((poly, 100));
80    ///
81    /// let poly_cmp = ModulusPolynomialRingZq::from_str("4  0 1 2 1 mod 100").unwrap();
82    /// assert_eq!(poly_cmp, mod_poly);
83    /// ```
84    ///
85    /// # Panics ...
86    /// - if `modulus` is smaller than `2`, or
87    /// - if the modulus polynomial is of degree smaller than `1`.
88    fn from((poly, modulus): (PolyOverZ, Mod)) -> Self {
89        let poly_zq = PolyOverZq::from((poly, modulus));
90
91        check_poly_mod(&poly_zq).unwrap();
92
93        Self::from(poly_zq)
94    }
95}
96
97impl From<&PolyOverZq> for ModulusPolynomialRingZq {
98    /// Creates a Modulus object of type [`ModulusPolynomialRingZq`]
99    /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq)
100    ///
101    /// Parameters:
102    /// - `poly`: the polynomial which is used as the modulus.
103    ///
104    /// Returns a new [`ModulusPolynomialRingZq`] object with the coefficients
105    /// and modulus from the [`PolyOverZq`] instance.
106    ///
107    /// # Examples
108    /// ```
109    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
110    /// use qfall_math::integer_mod_q::PolyOverZq;
111    /// use std::str::FromStr;
112    ///
113    /// let poly = PolyOverZq::from_str("3  1 0 1 mod 17").unwrap();
114    /// let mod_poly = ModulusPolynomialRingZq::try_from(&poly).unwrap();
115    /// ```
116    ///
117    /// # Panics ...
118    /// - if `modulus` is smaller than `2`, or
119    /// - if the modulus polynomial is of degree smaller than `1`.
120    fn from(poly: &PolyOverZq) -> Self {
121        check_poly_mod(poly).unwrap();
122        let mut non_zero = Vec::new();
123        for i in 0..poly.get_degree() {
124            let coeff: Z = poly.get_coeff(i).unwrap();
125            if coeff != 0 {
126                non_zero.push(i.try_into().unwrap());
127            }
128        }
129        Self {
130            modulus: Rc::new(poly.clone()),
131            ntt_basis: Rc::new(None),
132            non_zero,
133        }
134    }
135}
136
137implement_for_owned!(PolyOverZq, ModulusPolynomialRingZq, From);
138
139impl From<&ModulusPolynomialRingZq> for ModulusPolynomialRingZq {
140    // Only the smart pointer is increased here.
141
142    /// Alias for [`ModulusPolynomialRingZq::clone`].
143    fn from(value: &ModulusPolynomialRingZq) -> Self {
144        value.clone()
145    }
146}
147
148impl FromStr for ModulusPolynomialRingZq {
149    type Err = MathError;
150
151    /// Creates a Modulus object of type [`ModulusPolynomialRingZq`]
152    /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq). This first
153    /// converts the provided string into a [`PolyOverZq`] and then into the Modulus object.
154    /// It requires that the leading coefficient is `1`.
155    ///
156    /// **Warning**: If the input string starts with a correctly formatted
157    /// [`PolyOverZ`](crate::integer::PolyOverZ) object, the rest of the string
158    /// until the `"mod"` is ignored. This means that the input string
159    /// `"4  0 1 2 1 mod 13"` is the same as `"4  0 1 2 1 4 5 6 7 mod 13"`.
160    ///
161    /// Parameters:
162    /// - `s`: has to be a valid string to create a [`PolyOverZq`].
163    ///   For further information see [`PolyOverZq::from_str`].
164    ///
165    /// Note that the `[#number of coefficients]` and `[0th coefficient]`
166    /// are divided by two spaces and the string for the polynomial is trimmed,
167    /// i.e. all whitespaces before around the polynomial and the modulus are ignored.
168    ///
169    /// Returns a [`ModulusPolynomialRingZq`] or an error if the provided string was not
170    /// formatted correctly or the modulus was smaller than `2`.
171    ///
172    /// # Examples
173    /// ```
174    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
175    /// use std::str::FromStr;
176    ///
177    /// let poly_mod = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 17").unwrap();
178    /// ```
179    /// # Errors and Failures
180    /// - Returns a [`MathError`] of type
181    ///   [`StringConversionError`](MathError::StringConversionError)
182    ///     - if the provided first half of the string was not formatted correctly to
183    ///       create a [`PolyOverZ`],
184    ///     - if the provided second half of the
185    ///       string was not formatted correctly to create a [`Modulus`],
186    ///     - if the number of coefficients was smaller than the number provided
187    ///       at the start of the provided string,
188    ///     - if the provided value did not contain two whitespaces, or
189    ///     - if the delimiter `mod` could not be found.
190    ///     - For further information see [`PolyOverZq::from_str`].
191    /// - Returns a [`MathError`] of type
192    ///   [`InvalidModulus`](MathError::InvalidModulus)
193    ///     - if `modulus` is smaller than `2`, or
194    ///     - if the modulus polynomial is of degree smaller than `1`.
195    fn from_str(s: &str) -> Result<Self, Self::Err> {
196        let poly_zq = PolyOverZq::from_str(s)?;
197
198        check_poly_mod(&poly_zq)?;
199
200        Ok(Self::from(poly_zq))
201    }
202}
203
204/// Checks weather a given [`PolyOverZq`] can be used as a [`ModulusPolynomialRingZq`].
205/// It requires that the leading coefficient is `1`.
206///
207/// Parameters:
208/// - `poly_zq`: the [`PolyOverZq`] value that should be checked.
209///
210/// Returns an empty `Ok`, or an [`MathError`] if the polynomial is no valid modulus polynomial.
211///
212/// # Examples
213/// ```compile_fail
214/// use qfall_math::integer_mod_q::PolyOverZq;
215/// use std::str::FromStr;
216///
217/// let poly_zq = PolyOverZq::from_str("2  1 1 mod 17").unwrap();
218///
219/// check_poly_mod(&poly_zq)?
220/// ```
221///
222/// # Errors and Failures
223/// - Returns a [`MathError`] of type
224///   [`InvalidModulus`](MathError::InvalidModulus)
225///   if the modulus polynomial is of degree less than `1`.
226pub(crate) fn check_poly_mod(poly_zq: &PolyOverZq) -> Result<(), MathError> {
227    let leading_coefficient: Z = poly_zq.get_coeff(poly_zq.get_degree())?;
228    if poly_zq.get_degree() < 1 {
229        return Err(MathError::InvalidModulus(poly_zq.to_string()));
230    }
231    if leading_coefficient != 1 {
232        return Err(MathError::InvalidModulus(format!(
233            ". The leading coefficient needs to be 1, but it is {leading_coefficient}"
234        )));
235    }
236
237    Ok(())
238}
239
240/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
241/// since the format is reused, we omit some tests
242#[cfg(test)]
243mod test_availability {
244    use super::*;
245    use crate::integer_mod_q::PolyOverZq;
246    use std::str::FromStr;
247
248    /// Ensure that the from function can be called with several types.
249    #[test]
250    fn availability() {
251        let poly = PolyOverZ::from_str("2  1 1").unwrap();
252        let poly_zq = PolyOverZq::from_str("4  1 0 0 1 mod 17").unwrap();
253
254        let _ = ModulusPolynomialRingZq::from((&poly, 5));
255        let _ = ModulusPolynomialRingZq::from((poly.clone(), 5));
256
257        let _ = ModulusPolynomialRingZq::from(&poly_zq);
258        let _ = ModulusPolynomialRingZq::from(poly_zq);
259    }
260}
261
262/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
263/// since the format is reused, we omit some tests
264#[cfg(test)]
265mod test_try_from_poly_z {
266    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
267    use std::str::FromStr;
268
269    /// Ensure that primes and non-primes work as modulus
270    #[test]
271    fn poly_z_primes() {
272        let poly_z = PolyOverZ::from_str("2  2 1").unwrap();
273
274        let _ = ModulusPolynomialRingZq::from((&poly_z, 10));
275        let _ = ModulusPolynomialRingZq::from((poly_z, 11));
276    }
277
278    /// Ensure that the function panics if the modulus polynomial is 0
279    #[test]
280    #[should_panic]
281    fn panic_0() {
282        let poly = PolyOverZ::from(0);
283
284        let _ = ModulusPolynomialRingZq::from((poly, 17));
285    }
286}
287
288/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
289/// since the format is reused, we omit some tests
290#[cfg(test)]
291mod test_try_from_integer_mod {
292    use std::str::FromStr;
293
294    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
295
296    /// Ensure that primes and non-primes work as modulus
297    #[test]
298    fn mod_primes() {
299        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
300        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 18").unwrap();
301    }
302
303    /// Ensure that the function panics if the modulus polynomial is a constant polynomial
304    #[test]
305    #[should_panic]
306    fn panic_degree_0() {
307        let poly = PolyOverZ::from_str("1 1").unwrap();
308        let _ = ModulusPolynomialRingZq::from((poly, 17));
309    }
310}
311
312/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
313/// since the format is reused, we omit some tests
314#[cfg(test)]
315mod test_try_from_poly_zq {
316    use crate::{integer_mod_q::ModulusPolynomialRingZq, integer_mod_q::PolyOverZq};
317    use std::str::FromStr;
318
319    /// Ensure that it works with large coefficients
320    #[test]
321    fn working_large_entries() {
322        let poly_mod =
323            PolyOverZq::from_str(&format!("4  0 1 {} 1 mod {}", u64::MAX, 2_i32.pow(16) + 1))
324                .unwrap();
325        let _ = ModulusPolynomialRingZq::from(&poly_mod);
326    }
327
328    /// Ensure that primes and non-primes work as modulus
329    #[test]
330    fn poly_zq_primes() {
331        let poly_zq_1 = PolyOverZq::from_str("2  1 1 mod 10").unwrap();
332        let poly_zq_2 = PolyOverZq::from_str("2  1 1 mod 11").unwrap();
333
334        let _ = ModulusPolynomialRingZq::from(poly_zq_1);
335        let _ = ModulusPolynomialRingZq::from(poly_zq_2);
336    }
337
338    /// Ensure that the function panics if the modulus polynomial is 0
339    #[test]
340    #[should_panic]
341    fn panic_0() {
342        let poly = PolyOverZq::from((0, 17));
343
344        let _ = ModulusPolynomialRingZq::from(poly);
345    }
346}
347
348/// most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
349/// since the format is reused, we omit some tests
350#[cfg(test)]
351mod test_from_str {
352    use crate::integer_mod_q::ModulusPolynomialRingZq;
353    use std::str::FromStr;
354
355    /// Ensure that at input of a wrong format an error is returned
356    #[test]
357    fn wrong_modulus_fmt() {
358        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod -17").is_err());
359        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 17 mod 42").is_err());
360        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 0").is_err());
361        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 1 7").is_err());
362        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod ba").is_err());
363    }
364
365    /// Ensure that large coefficients work
366    #[test]
367    fn working_large_entries() {
368        assert!(
369            ModulusPolynomialRingZq::from_str(&format!(
370                "4  0 1 {} 1 mod {}",
371                u64::MAX,
372                2_i32.pow(16) + 1
373            ))
374            .is_ok()
375        );
376    }
377
378    /// Ensure that primes and non-primes work as modulus
379    #[test]
380    fn poly_zq_primes() {
381        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 1 mod 10").is_ok());
382        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 1 mod 11").is_ok());
383    }
384}