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}