qfall_tools/utils/
common_moduli.rs

1// Copyright © 2023 Niklas Siemer
2//
3// This file is part of qFALL-tools.
4//
5// qFALL-tools 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//! Contains functions to quickly instantiate
10//! common moduli for ring-based lattice cryptography.
11
12use qfall_math::{
13    error::MathError,
14    integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolyOverZq},
15    traits::SetCoefficient,
16};
17use std::fmt::Display;
18
19/// Outputs a [`ModulusPolynomialRingZq`] of the form `X^n + 1 mod q`.
20///
21/// Parameters:
22/// - `n`: specifies the degree of the modulus polynomial
23/// - `q`: specifies the modulus of the modulus polynomial
24///
25/// Returns a [`ModulusPolynomialRingZq`] of the form `X^n + 1 mod q` or
26/// a [`MathError`] if `q <= 1`, `n < 0`, or `n` does not into an [`i64`].
27///
28/// # Examples
29/// ```
30/// use qfall_tools::utils::common_moduli::new_anticyclic;
31///
32/// let poly_mod = new_anticyclic(8, 17);
33/// ```
34///
35/// # Errors and Failures
36/// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) if
37///   the `n` is negative or it does not fit into an [`i64`].
38///
39/// # Panics ...
40/// - if the `q` is not larger than `1`.
41pub fn new_anticyclic(
42    n: impl TryInto<i64> + Display,
43    q: impl Into<Modulus>,
44) -> Result<ModulusPolynomialRingZq, MathError> {
45    let mut poly = PolyOverZq::from((1, q));
46    poly.set_coeff(n, 1)?;
47    Ok(ModulusPolynomialRingZq::from(&poly))
48}
49
50/// Outputs a [`ModulusPolynomialRingZq`] of the form `X^n - 1 mod q`.
51///
52/// Parameters:
53/// - `n`: specifies the degree of the modulus polynomial
54/// - `q`: specifies the modulus of the modulus polynomial
55///
56/// Returns a [`ModulusPolynomialRingZq`] of the form `X^n - 1 mod q` or
57/// a [`MathError`] if `q <= 1`, `n < 0`, or `n` does not into an [`i64`].
58///
59/// # Examples
60/// ```
61/// use qfall_tools::utils::common_moduli::new_cyclic;
62///
63/// let poly_mod = new_cyclic(8, 17);
64/// ```
65///
66/// # Errors and Failures
67/// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) if
68///   the `n` is negative or it does not fit into an [`i64`].
69///
70/// # Panics ...
71/// - if the `q` is not larger than `1`.
72pub fn new_cyclic(
73    n: impl TryInto<i64> + Display,
74    q: impl Into<Modulus>,
75) -> Result<ModulusPolynomialRingZq, MathError> {
76    let mut poly = PolyOverZq::from((-1, q));
77    poly.set_coeff(n, 1)?;
78    Ok(ModulusPolynomialRingZq::from(&poly))
79}
80
81#[cfg(test)]
82mod test_new_anticyclic {
83    use super::new_anticyclic;
84    use qfall_math::{integer::Z, integer_mod_q::PolyOverZq, traits::GetCoefficient};
85
86    /// Ensure that the modulus polynomial has the specified degree.
87    #[test]
88    fn degree() {
89        let degrees = [1, 4, 7, 16, 32, 128];
90        for degree in degrees {
91            let poly_mod = new_anticyclic(degree, 7).unwrap();
92
93            assert_eq!(degree, poly_mod.get_degree());
94        }
95    }
96
97    /// Check whether the method outputs the correct polynomial.
98    #[test]
99    fn correct_polynomial() {
100        let degrees = [1, 4, 7, 16, 32, 128];
101        for degree in degrees {
102            let poly_mod = new_anticyclic(degree, 7).unwrap();
103            let poly_zq = PolyOverZq::from(&poly_mod);
104
105            assert_eq!(
106                Z::ONE,
107                GetCoefficient::<Z>::get_coeff(&poly_zq, degree).unwrap()
108            );
109            assert_eq!(Z::ONE, GetCoefficient::<Z>::get_coeff(&poly_zq, 0).unwrap());
110            for i in 1..degree {
111                assert_eq!(
112                    Z::ZERO,
113                    GetCoefficient::<Z>::get_coeff(&poly_zq, i).unwrap()
114                );
115            }
116        }
117    }
118
119    /// Ensures that the correct modulus is set as
120    /// the integer modulus of the output modulus polynomial.
121    #[test]
122    fn correct_modulus() {
123        let moduli = [7, 10, i64::MAX];
124        for modulus in moduli {
125            let poly_mod = new_anticyclic(2, modulus).unwrap();
126
127            assert_eq!(Z::from(modulus), poly_mod.get_q());
128        }
129    }
130
131    /// Ensures that an invalid degree for the modulus polynomial results in an error.
132    #[test]
133    fn invalid_n() {
134        let res = new_anticyclic(-1, 7);
135
136        assert!(res.is_err());
137    }
138
139    /// Ensures that an invalid modulus for the modulus polynomial results in a panic.
140    #[test]
141    #[should_panic]
142    fn invalid_modulus() {
143        let _ = new_anticyclic(2, 0);
144    }
145}
146
147#[cfg(test)]
148mod test_new_cyclic {
149    use super::new_cyclic;
150    use qfall_math::{integer::Z, integer_mod_q::PolyOverZq, traits::GetCoefficient};
151
152    /// Ensure that the modulus polynomial has the specified degree.
153    #[test]
154    fn degree() {
155        let degrees = [1, 4, 7, 16, 32, 128];
156        for degree in degrees {
157            let poly_mod = new_cyclic(degree, 7).unwrap();
158
159            assert_eq!(degree, poly_mod.get_degree());
160        }
161    }
162
163    /// Check whether the method outputs the correct polynomial.
164    #[test]
165    fn correct_polynomial() {
166        let degrees = [1, 4, 7, 16, 32, 128];
167        for degree in degrees {
168            let poly_mod = new_cyclic(degree, 7).unwrap();
169            let poly_zq = PolyOverZq::from(&poly_mod);
170
171            assert_eq!(
172                Z::ONE,
173                GetCoefficient::<Z>::get_coeff(&poly_zq, degree).unwrap()
174            );
175            assert_eq!(
176                Z::from(6),
177                GetCoefficient::<Z>::get_coeff(&poly_zq, 0).unwrap()
178            );
179            for i in 1..degree {
180                assert_eq!(
181                    Z::ZERO,
182                    GetCoefficient::<Z>::get_coeff(&poly_zq, i).unwrap()
183                );
184            }
185        }
186    }
187
188    /// Ensures that the correct modulus is set as
189    /// the integer modulus of the output modulus polynomial.
190    #[test]
191    fn correct_modulus() {
192        let moduli = [7, 10, i64::MAX];
193        for modulus in moduli {
194            let poly_mod = new_cyclic(2, modulus).unwrap();
195
196            assert_eq!(Z::from(modulus), poly_mod.get_q());
197        }
198    }
199
200    /// Ensures that an invalid degree for the modulus polynomial results in an error.
201    #[test]
202    fn invalid_n() {
203        let res = new_cyclic(-1, 7);
204
205        assert!(res.is_err());
206    }
207
208    /// Ensures that an invalid modulus for the modulus polynomial results in a panic.
209    #[test]
210    #[should_panic]
211    fn invalid_modulus() {
212        let _ = new_cyclic(2, 0);
213    }
214}