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 modulus polynomial is of degree smaller than `1`.
51    /// - if the leading coefficient is not `1.`
52    fn from((poly, modulus): (&PolyOverZ, Mod)) -> Self {
53        let poly_zq = PolyOverZq::from((poly, modulus));
54
55        check_poly_mod(&poly_zq).unwrap();
56
57        Self::from(poly_zq)
58    }
59}
60
61impl<Mod: Into<Modulus>> From<(PolyOverZ, Mod)> for ModulusPolynomialRingZq {
62    /// Creates a [`ModulusPolynomialRingZq`] from a [`PolyOverZ`] and a value that implements [`Into<Modulus>`].
63    /// It requires that the leading coefficient is `1`.
64    ///
65    /// Parameters:
66    /// - `poly`: the coefficients of the polynomial.
67    /// - `modulus`: the modulus by which each entry is reduced.
68    ///
69    /// Returns a new [`ModulusPolynomialRingZq`] with the coefficients from the
70    /// [`PolyOverZ`] instance under the specified [`Modulus`] value.
71    ///
72    /// # Examples
73    /// ```
74    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
75    /// use qfall_math::integer::PolyOverZ;
76    /// use std::str::FromStr;
77    ///
78    /// let poly = PolyOverZ::from_str("4  0 1 102 1").unwrap();
79    ///
80    /// let mod_poly = ModulusPolynomialRingZq::from((poly, 100));
81    ///
82    /// let poly_cmp = ModulusPolynomialRingZq::from_str("4  0 1 2 1 mod 100").unwrap();
83    /// assert_eq!(poly_cmp, mod_poly);
84    /// ```
85    ///
86    /// # Panics ...
87    /// - if `modulus` is smaller than `2`, or
88    /// - if the modulus polynomial is of degree smaller than `1`.
89    /// - if the leading coefficient is not `1.`
90    fn from((poly, modulus): (PolyOverZ, Mod)) -> Self {
91        let poly_zq = PolyOverZq::from((poly, modulus));
92
93        check_poly_mod(&poly_zq).unwrap();
94
95        Self::from(poly_zq)
96    }
97}
98
99impl From<&PolyOverZq> for ModulusPolynomialRingZq {
100    /// Creates a Modulus object of type [`ModulusPolynomialRingZq`]
101    /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq).
102    /// It requires that the leading coefficient is `1`.
103    ///
104    /// Parameters:
105    /// - `poly`: the polynomial which is used as the modulus.
106    ///
107    /// Returns a new [`ModulusPolynomialRingZq`] object with the coefficients
108    /// and modulus from the [`PolyOverZq`] instance.
109    ///
110    /// # Examples
111    /// ```
112    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
113    /// use qfall_math::integer_mod_q::PolyOverZq;
114    /// use std::str::FromStr;
115    ///
116    /// let poly = PolyOverZq::from_str("3  1 0 1 mod 17").unwrap();
117    /// let mod_poly = ModulusPolynomialRingZq::try_from(&poly).unwrap();
118    /// ```
119    ///
120    /// # Panics ...
121    /// - if `modulus` is smaller than `2`, or
122    /// - if the modulus polynomial is of degree smaller than `1`.
123    /// - if the leading coefficient is not `1.`
124    fn from(poly: &PolyOverZq) -> Self {
125        check_poly_mod(poly).unwrap();
126
127        let non_zero = non_zero_positions(poly);
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    ///     - if the leading coefficient is not `1`.
196    fn from_str(s: &str) -> Result<Self, Self::Err> {
197        let poly_zq = PolyOverZq::from_str(s)?;
198
199        check_poly_mod(&poly_zq)?;
200
201        Ok(Self::from(poly_zq))
202    }
203}
204
205/// Fills a vector with the position of all non-zero coefficients in a [`PolyOverZq`] except the leading coefficient.
206///
207/// Parameters:
208/// - `poly`: defines the polynomial whose positions of non-zero coefficients are output
209///
210/// Returns a [`Vec<usize>`] containing the positions of all non-zero coefficients except the leading coefficient.
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("4  1 0 0 1 mod 17").unwrap();
218///
219/// let non_zero = non_zero_positions(&poly_zq);
220/// ```
221pub(crate) fn non_zero_positions(poly: &PolyOverZq) -> Vec<usize> {
222    let mut non_zero = Vec::new();
223    for i in 0..poly.get_degree() {
224        let coeff: Z = poly.get_coeff(i).unwrap();
225        if coeff != 0 {
226            non_zero.push(i.try_into().unwrap());
227        }
228    }
229    non_zero
230}
231
232/// Checks weather a given [`PolyOverZq`] can be used as a [`ModulusPolynomialRingZq`].
233/// It requires that the leading coefficient is `1`.
234///
235/// Parameters:
236/// - `poly_zq`: the [`PolyOverZq`] value that should be checked.
237///
238/// Returns an empty `Ok`, or an [`MathError`] if the polynomial is no valid modulus polynomial.
239///
240/// # Examples
241/// ```compile_fail
242/// use qfall_math::integer_mod_q::PolyOverZq;
243/// use std::str::FromStr;
244///
245/// let poly_zq = PolyOverZq::from_str("2  1 1 mod 17").unwrap();
246///
247/// check_poly_mod(&poly_zq)?
248/// ```
249///
250/// # Errors and Failures
251/// - Returns a [`MathError`] of type
252///   [`InvalidModulus`](MathError::InvalidModulus)
253///   if the modulus polynomial is of degree less than `1`.
254/// - Returns a [`MathError`] of type
255///   [`InvalidModulus`](MathError::InvalidModulus)
256///   if the leading coefficient is not `1`.
257pub(crate) fn check_poly_mod(poly_zq: &PolyOverZq) -> Result<(), MathError> {
258    let leading_coefficient: Z = poly_zq.get_coeff(poly_zq.get_degree())?;
259    if poly_zq.get_degree() < 1 {
260        return Err(MathError::InvalidModulus(poly_zq.to_string()));
261    }
262    if leading_coefficient != 1 {
263        return Err(MathError::InvalidModulus(format!(
264            ". The leading coefficient needs to be 1, but it is {leading_coefficient}"
265        )));
266    }
267
268    Ok(())
269}
270
271/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
272/// since the format is reused, we omit some tests
273#[cfg(test)]
274mod test_availability {
275    use super::*;
276    use crate::integer_mod_q::PolyOverZq;
277    use std::str::FromStr;
278
279    /// Ensure that the from function can be called with several types.
280    #[test]
281    fn availability() {
282        let poly = PolyOverZ::from_str("2  1 1").unwrap();
283        let poly_zq = PolyOverZq::from_str("4  1 0 0 1 mod 17").unwrap();
284
285        let _ = ModulusPolynomialRingZq::from((&poly, 5));
286        let _ = ModulusPolynomialRingZq::from((poly.clone(), 5));
287
288        let _ = ModulusPolynomialRingZq::from(&poly_zq);
289        let _ = ModulusPolynomialRingZq::from(poly_zq);
290    }
291}
292
293/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
294/// since the format is reused, we omit some tests
295#[cfg(test)]
296mod test_try_from_poly_z {
297    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
298    use std::str::FromStr;
299
300    /// Ensure that primes and non-primes work as modulus
301    #[test]
302    fn poly_z_primes() {
303        let poly_z = PolyOverZ::from_str("2  2 1").unwrap();
304
305        let _ = ModulusPolynomialRingZq::from((&poly_z, 10));
306        let _ = ModulusPolynomialRingZq::from((poly_z, 11));
307    }
308
309    /// Ensure that the function panics if the modulus polynomial is 0
310    #[test]
311    #[should_panic]
312    fn panic_0() {
313        let poly = PolyOverZ::from(0);
314
315        let _ = ModulusPolynomialRingZq::from((poly, 17));
316    }
317}
318
319/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
320/// since the format is reused, we omit some tests
321#[cfg(test)]
322mod test_try_from_integer_mod {
323    use std::str::FromStr;
324
325    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
326
327    /// Ensure that primes and non-primes work as modulus
328    #[test]
329    fn mod_primes() {
330        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
331        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 18").unwrap();
332    }
333
334    /// Ensure that the function panics if the modulus polynomial is a constant polynomial
335    #[test]
336    #[should_panic]
337    fn panic_degree_0() {
338        let poly = PolyOverZ::from_str("1 1").unwrap();
339        let _ = ModulusPolynomialRingZq::from((poly, 17));
340    }
341}
342
343/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
344/// since the format is reused, we omit some tests
345#[cfg(test)]
346mod test_try_from_poly_zq {
347    use crate::{integer_mod_q::ModulusPolynomialRingZq, integer_mod_q::PolyOverZq};
348    use std::str::FromStr;
349
350    /// Ensure that it works with large coefficients
351    #[test]
352    fn working_large_entries() {
353        let poly_mod =
354            PolyOverZq::from_str(&format!("4  0 1 {} 1 mod {}", u64::MAX, 2_i32.pow(16) + 1))
355                .unwrap();
356        let _ = ModulusPolynomialRingZq::from(&poly_mod);
357    }
358
359    /// Ensure that primes and non-primes work as modulus
360    #[test]
361    fn poly_zq_primes() {
362        let poly_zq_1 = PolyOverZq::from_str("2  1 1 mod 10").unwrap();
363        let poly_zq_2 = PolyOverZq::from_str("2  1 1 mod 11").unwrap();
364
365        let _ = ModulusPolynomialRingZq::from(poly_zq_1);
366        let _ = ModulusPolynomialRingZq::from(poly_zq_2);
367    }
368
369    /// Ensure that the function panics if the modulus polynomial is 0
370    #[test]
371    #[should_panic]
372    fn panic_0() {
373        let poly = PolyOverZq::from((0, 17));
374
375        let _ = ModulusPolynomialRingZq::from(poly);
376    }
377
378    /// Ensure that the function panics if the leading coefficient is not `1`
379    #[test]
380    #[should_panic]
381    fn panic_leading_coefficient() {
382        let poly = PolyOverZq::from_str("2  1 2 mod 10").unwrap();
383
384        let _ = ModulusPolynomialRingZq::from(poly);
385    }
386}
387
388/// most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
389/// since the format is reused, we omit some tests
390#[cfg(test)]
391mod test_from_str {
392    use crate::integer_mod_q::ModulusPolynomialRingZq;
393    use std::str::FromStr;
394
395    /// Ensure that at input of a wrong format an error is returned
396    #[test]
397    fn wrong_modulus_fmt() {
398        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod -17").is_err());
399        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 17 mod 42").is_err());
400        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 0").is_err());
401        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 1 7").is_err());
402        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod ba").is_err());
403    }
404
405    /// Ensure that large coefficients work
406    #[test]
407    fn working_large_entries() {
408        assert!(
409            ModulusPolynomialRingZq::from_str(&format!(
410                "4  0 1 {} 1 mod {}",
411                u64::MAX,
412                2_i32.pow(16) + 1
413            ))
414            .is_ok()
415        );
416    }
417
418    /// Ensure that primes and non-primes work as modulus
419    #[test]
420    fn poly_zq_primes() {
421        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 1 mod 10").is_ok());
422        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 1 mod 11").is_ok());
423    }
424}