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}