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}