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,
17    integer_mod_q::{Modulus, PolyOverZq},
18    macros::for_others::implement_for_owned,
19};
20use flint_sys::fq::fq_ctx_init_modulus;
21use std::{ffi::CString, mem::MaybeUninit, 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    ///
26    /// Parameters:
27    /// - `poly`: the coefficients of the polynomial.
28    /// - `modulus`: the modulus by which each entry is reduced.
29    ///
30    /// Returns a new [`ModulusPolynomialRingZq`] with the coefficients from the
31    /// [`PolyOverZ`] instance under the specified [`Modulus`] value.
32    ///
33    /// # Examples
34    /// ```
35    /// use qfall_math::integer_mod_q::{ModulusPolynomialRingZq, Modulus};
36    /// use qfall_math::integer::PolyOverZ;
37    /// use std::str::FromStr;
38    ///
39    /// let poly = PolyOverZ::from_str("4  0 1 102 3").unwrap();
40    ///
41    /// let mod_poly = ModulusPolynomialRingZq::from((&poly, 100));
42    ///
43    /// let poly_cmp = ModulusPolynomialRingZq::from_str("4  0 1 2 3 mod 100").unwrap();
44    /// assert_eq!(poly_cmp, mod_poly);
45    /// ```
46    ///
47    /// # Panics ...
48    /// - if `modulus` is smaller than `2`, or
49    /// - if the degree of the polynomial is smaller than `1`.
50    fn from((poly, modulus): (&PolyOverZ, Mod)) -> Self {
51        let poly_zq = PolyOverZq::from((poly, modulus));
52
53        check_poly_mod(&poly_zq).unwrap();
54
55        Self::from(poly_zq)
56    }
57}
58
59impl<Mod: Into<Modulus>> From<(PolyOverZ, Mod)> for ModulusPolynomialRingZq {
60    /// Creates a [`ModulusPolynomialRingZq`] from a [`PolyOverZ`] and a value that implements [`Into<Modulus>`].
61    ///
62    /// Parameters:
63    /// - `poly`: the coefficients of the polynomial.
64    /// - `modulus`: the modulus by which each entry is reduced.
65    ///
66    /// Returns a new [`ModulusPolynomialRingZq`] with the coefficients from the
67    /// [`PolyOverZ`] instance under the specified [`Modulus`] value.
68    ///
69    /// # Examples
70    /// ```
71    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
72    /// use qfall_math::integer::PolyOverZ;
73    /// use std::str::FromStr;
74    ///
75    /// let poly = PolyOverZ::from_str("4  0 1 102 3").unwrap();
76    ///
77    /// let mod_poly = ModulusPolynomialRingZq::from((poly, 100));
78    ///
79    /// let poly_cmp = ModulusPolynomialRingZq::from_str("4  0 1 2 3 mod 100").unwrap();
80    /// assert_eq!(poly_cmp, mod_poly);
81    /// ```
82    ///
83    /// # Panics ...
84    /// - if `modulus` is smaller than `2`, or
85    /// - if the modulus polynomial is of degree smaller than `1`.
86    fn from((poly, modulus): (PolyOverZ, Mod)) -> Self {
87        let poly_zq = PolyOverZq::from((poly, modulus));
88
89        check_poly_mod(&poly_zq).unwrap();
90
91        Self::from(poly_zq)
92    }
93}
94
95impl From<&PolyOverZq> for ModulusPolynomialRingZq {
96    /// Creates a Modulus object of type [`ModulusPolynomialRingZq`]
97    /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq)
98    ///
99    /// Parameters:
100    /// - `poly`: the polynomial which is used as the modulus.
101    ///
102    /// Returns a new [`ModulusPolynomialRingZq`] object with the coefficients
103    /// and modulus from the [`PolyOverZq`] instance.
104    ///
105    /// # Examples
106    /// ```
107    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
108    /// use qfall_math::integer_mod_q::PolyOverZq;
109    /// use std::str::FromStr;
110    ///
111    /// let poly = PolyOverZq::from_str("3  1 0 1 mod 17").unwrap();
112    /// let mod_poly = ModulusPolynomialRingZq::try_from(&poly).unwrap();
113    /// ```
114    ///
115    /// # Panics ...
116    /// - if `modulus` is smaller than `2`, or
117    /// - if the modulus polynomial is of degree smaller than `1`.
118    fn from(poly: &PolyOverZq) -> Self {
119        check_poly_mod(poly).unwrap();
120
121        let mut modulus = MaybeUninit::uninit();
122        let c_string = CString::new("X").unwrap();
123        unsafe {
124            fq_ctx_init_modulus(
125                modulus.as_mut_ptr(),
126                &poly.poly,
127                poly.modulus.get_fmpz_mod_ctx_struct(),
128                c_string.as_ptr(),
129            );
130            Self {
131                modulus: Rc::new(modulus.assume_init()),
132                ntt_basis: Rc::new(None),
133            }
134        }
135    }
136}
137
138implement_for_owned!(PolyOverZq, ModulusPolynomialRingZq, From);
139
140impl From<&ModulusPolynomialRingZq> for ModulusPolynomialRingZq {
141    // Only the smart pointer is increased here.
142
143    /// Alias for [`ModulusPolynomialRingZq::clone`].
144    fn from(value: &ModulusPolynomialRingZq) -> Self {
145        value.clone()
146    }
147}
148
149impl FromStr for ModulusPolynomialRingZq {
150    type Err = MathError;
151
152    /// Creates a Modulus object of type [`ModulusPolynomialRingZq`]
153    /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq). This first
154    /// converts the provided string into a [`PolyOverZq`] and then into the Modulus object.
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 3 mod 13"` is the same as `"4  0 1 2 3 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///
206/// Parameters:
207/// - `poly_zq`: the [`PolyOverZq`] value that should be checked.
208///
209/// Returns an empty `Ok`, or an [`MathError`] if the polynomial is no valid modulus polynomial.
210///
211/// # Examples
212/// ```compile_fail
213/// use qfall_math::integer_mod_q::PolyOverZq;
214/// use std::str::FromStr;
215///
216/// let poly_zq = PolyOverZq::from_str("2  1 2 mod 17").unwrap();
217///
218/// check_poly_mod(&poly_zq)?
219/// ```
220///
221/// # Errors and Failures
222/// - Returns a [`MathError`] of type
223///   [`InvalidModulus`](MathError::InvalidModulus)
224///   if the modulus polynomial is of degree less than `1`.
225pub(crate) fn check_poly_mod(poly_zq: &PolyOverZq) -> Result<(), MathError> {
226    if poly_zq.get_degree() < 1 {
227        return Err(MathError::InvalidModulus(poly_zq.to_string()));
228    }
229
230    Ok(())
231}
232
233/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
234/// since the format is reused, we omit some tests
235#[cfg(test)]
236mod test_availability {
237    use super::*;
238    use crate::integer_mod_q::PolyOverZq;
239    use std::str::FromStr;
240
241    /// Ensure that the from function can be called with several types.
242    #[test]
243    fn availability() {
244        let poly = PolyOverZ::from_str("2  1 1").unwrap();
245        let poly_zq = PolyOverZq::from_str("4  1 0 0 1 mod 17").unwrap();
246
247        let _ = ModulusPolynomialRingZq::from((&poly, 5));
248        let _ = ModulusPolynomialRingZq::from((poly.clone(), 5));
249
250        let _ = ModulusPolynomialRingZq::from(&poly_zq);
251        let _ = ModulusPolynomialRingZq::from(poly_zq);
252    }
253}
254
255/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
256/// since the format is reused, we omit some tests
257#[cfg(test)]
258mod test_try_from_poly_z {
259    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
260    use std::str::FromStr;
261
262    /// Ensure that primes and non-primes work as modulus
263    #[test]
264    fn poly_z_primes() {
265        let poly_z = PolyOverZ::from_str("2  2 2").unwrap();
266
267        let _ = ModulusPolynomialRingZq::from((&poly_z, 10));
268        let _ = ModulusPolynomialRingZq::from((poly_z, 11));
269    }
270
271    /// Ensure that the function panics if the modulus polynomial is 0
272    #[test]
273    #[should_panic]
274    fn panic_0() {
275        let poly = PolyOverZ::from(0);
276
277        let _ = ModulusPolynomialRingZq::from((poly, 17));
278    }
279}
280
281/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
282/// since the format is reused, we omit some tests
283#[cfg(test)]
284mod test_try_from_integer_mod {
285    use std::str::FromStr;
286
287    use crate::{integer::PolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
288
289    /// Ensure that primes and non-primes work as modulus
290    #[test]
291    fn mod_primes() {
292        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 17").unwrap();
293        let _ = ModulusPolynomialRingZq::from_str("3  3 0 1 mod 18").unwrap();
294    }
295
296    /// Ensure that the function panics if the modulus polynomial is 0
297    #[test]
298    #[should_panic]
299    fn panic_degree_1() {
300        let poly = PolyOverZ::from_str("1 5").unwrap();
301        let _ = ModulusPolynomialRingZq::from((poly, 17));
302    }
303}
304
305/// Most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
306/// since the format is reused, we omit some tests
307#[cfg(test)]
308mod test_try_from_poly_zq {
309    use crate::{integer_mod_q::ModulusPolynomialRingZq, integer_mod_q::PolyOverZq};
310    use std::str::FromStr;
311
312    /// Ensure that it works with large coefficients
313    #[test]
314    fn working_large_entries() {
315        let poly_mod =
316            PolyOverZq::from_str(&format!("4  0 1 -2 {} mod {}", u64::MAX, 2_i32.pow(16) + 1))
317                .unwrap();
318        let _ = ModulusPolynomialRingZq::from(&poly_mod);
319    }
320
321    /// Ensure that large entries work
322    #[test]
323    fn poly_zq_unchanged() {
324        let in_str = format!("4  0 1 3 {} mod {}", u64::MAX, 2_i32.pow(16) + 1);
325        let cmp_str = "3  0 1 3 mod 65537";
326        let poly_zq = PolyOverZq::from_str(&in_str).unwrap();
327        let _ = ModulusPolynomialRingZq::from(&poly_zq);
328        assert_eq!(cmp_str, poly_zq.to_string());
329    }
330
331    /// Ensure that primes and non-primes work as modulus
332    #[test]
333    fn poly_zq_primes() {
334        let poly_zq_1 = PolyOverZq::from_str("2  1 1 mod 10").unwrap();
335        let poly_zq_2 = PolyOverZq::from_str("2  1 1 mod 11").unwrap();
336
337        let _ = ModulusPolynomialRingZq::from(poly_zq_1);
338        let _ = ModulusPolynomialRingZq::from(poly_zq_2);
339    }
340
341    /// Ensure that the function panics if the modulus polynomial is 0
342    #[test]
343    #[should_panic]
344    fn panic_0() {
345        let poly = PolyOverZq::from((0, 17));
346
347        let _ = ModulusPolynomialRingZq::from(poly);
348    }
349}
350
351/// most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq)
352/// since the format is reused, we omit some tests
353#[cfg(test)]
354mod test_from_str {
355    use crate::integer_mod_q::ModulusPolynomialRingZq;
356    use std::str::FromStr;
357
358    /// Ensure that at input of a wrong format an error is returned
359    #[test]
360    fn wrong_modulus_fmt() {
361        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod -17").is_err());
362        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 17 mod 42").is_err());
363        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 0").is_err());
364        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod 1 7").is_err());
365        assert!(ModulusPolynomialRingZq::from_str("3  4 0 1 mod ba").is_err());
366    }
367
368    /// Ensure that large coefficients work
369    #[test]
370    fn working_large_entries() {
371        assert!(
372            ModulusPolynomialRingZq::from_str(&format!(
373                "4  0 1 3 {} mod {}",
374                u64::MAX,
375                2_i32.pow(16) + 1
376            ))
377            .is_ok()
378        );
379    }
380
381    /// Ensure that primes and non-primes work as modulus
382    #[test]
383    fn poly_zq_primes() {
384        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 2 mod 10").is_ok());
385        assert!(ModulusPolynomialRingZq::from_str("4  0 1 3 2 mod 11").is_ok());
386    }
387}