qfall_math/integer_mod_q/polynomial_ring_zq/properties.rs
1// Copyright © 2024 Marcel Luca Schmidt
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 check certain properties of [`PolynomialRingZq`].
10//! This includes checks such as reducibility.
11
12use super::PolynomialRingZq;
13use crate::integer_mod_q::{NTTPolynomialRingZq, PolyOverZq};
14
15impl PolynomialRingZq {
16 /// Checks if a [`PolynomialRingZq`] is irreducible.
17 ///
18 /// Returns `true` if the polynomial is irreducible and `false` otherwise.
19 ///
20 /// # Examples
21 /// ```
22 /// use qfall_math::integer_mod_q::PolynomialRingZq;
23 /// use std::str::FromStr;
24 ///
25 /// let poly_irr = PolynomialRingZq::from_str("2 1 1 / 3 1 2 3 mod 17").unwrap();
26 /// // returns true, since X + 1 is irreducible
27 /// assert!(poly_irr.is_irreducible());
28 /// ```
29 pub fn is_irreducible(&self) -> bool {
30 let poly_over_zq = PolyOverZq::from((&self.poly, self.modulus.get_q()));
31
32 // Since after reduction with the polynomial modulus, the check of
33 // irreducibility is the same as without the polynomial modulus
34 poly_over_zq.is_irreducible()
35 }
36
37 /// Checks if a [`PolynomialRingZq`] is the constant polynomial with coefficient `1`.
38 ///
39 /// Returns `true` if there is only one coefficient, which is `1`.
40 ///
41 /// # Examples
42 /// ```
43 /// use qfall_math::integer_mod_q::PolynomialRingZq;
44 /// use std::str::FromStr;
45 ///
46 /// let value = PolynomialRingZq::from_str("1 1 / 3 1 0 1 mod 4").unwrap();
47 /// assert!(value.is_one());
48 /// ```
49 pub fn is_one(&self) -> bool {
50 self.poly.is_one()
51 }
52
53 /// Checks if every entry of a [`PolynomialRingZq`] is `0`.
54 ///
55 /// Returns `true` if [`PolynomialRingZq`] has no coefficients.
56 ///
57 /// # Examples
58 /// ```
59 /// use qfall_math::integer_mod_q::PolynomialRingZq;
60 /// use std::str::FromStr;
61 ///
62 /// let value = PolynomialRingZq::from_str("0 / 2 1 1 mod 7").unwrap();
63 /// assert!(value.is_zero());
64 /// ```
65 pub fn is_zero(&self) -> bool {
66 self.poly.is_zero()
67 }
68
69 /// Computes the NTT representation of `self`.
70 ///
71 /// # Examples
72 /// ```
73 /// use qfall_math::integer_mod_q::{NTTPolynomialRingZq, PolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq};
74 /// use crate::qfall_math::traits::SetCoefficient;
75 /// use std::str::FromStr;
76 ///
77 /// let n = 4;
78 /// let modulus = 7681;
79 ///
80 /// let mut mod_poly = PolyOverZq::from(modulus);
81 /// mod_poly.set_coeff(0, 1).unwrap();
82 /// mod_poly.set_coeff(n, 1).unwrap();
83 ///
84 /// let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
85 /// polynomial_modulus.set_ntt_unchecked(1925);
86 ///
87 /// let poly_ring = PolynomialRingZq::sample_uniform(&polynomial_modulus);
88 ///
89 /// let ntt_poly_ring = poly_ring.ntt();
90 /// ```
91 ///
92 /// # Panics ...
93 /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq),
94 /// which is part of the [`ModulusPolynomialRingZq`](crate::integer_mod_q::ModulusPolynomialRingZq) in `self`
95 /// is not set.
96 pub fn ntt(&self) -> NTTPolynomialRingZq {
97 NTTPolynomialRingZq::from(self)
98 }
99}
100
101#[cfg(test)]
102mod test_is_irreducible {
103 use crate::integer_mod_q::PolynomialRingZq;
104 use std::str::FromStr;
105
106 /// Ensure that a irreducible [`PolynomialRingZq`] returns `true`.
107 #[test]
108 fn poly_is_irreducible() {
109 // 9X^2 + 12X + 10 is irreducible over 17
110 let poly_irr = PolynomialRingZq::from_str("3 10 12 9 / 4 1 10 12 9 mod 17").unwrap();
111 assert!(poly_irr.is_irreducible());
112 }
113
114 /// Ensure that a reducible [`PolynomialRingZq`] returns `false`.
115 #[test]
116 fn poly_is_reducible() {
117 let poly_irr = PolynomialRingZq::from_str("3 1 2 1 / 4 1 10 12 9 mod 17").unwrap();
118 assert!(!poly_irr.is_irreducible());
119 }
120}
121
122#[cfg(test)]
123mod test_is_one {
124 use super::PolynomialRingZq;
125 use std::str::FromStr;
126
127 /// Ensure that is_one returns `true` for the one polynomial.
128 #[test]
129 fn one_detection() {
130 let one = PolynomialRingZq::from_str("1 1 / 4 1 10 12 9 mod 7").unwrap();
131 let one_2 = PolynomialRingZq::from_str("2 1 14 / 4 1 10 12 9 mod 7").unwrap();
132 let one_3 = PolynomialRingZq::from_str("3 11 4 10 / 3 5 2 5 mod 11").unwrap();
133
134 assert!(one.is_one());
135 assert!(one_2.is_one());
136 assert!(one_3.is_one());
137 }
138
139 /// Ensure that is_one returns `false` for other polynomials.
140 #[test]
141 fn one_rejection() {
142 let small = PolynomialRingZq::from_str("4 1 0 0 1 / 4 1 10 12 9 mod 7").unwrap();
143 let large = PolynomialRingZq::from_str(&format!(
144 "1 {} / 4 1 10 12 9 mod {}",
145 (u128::MAX - 1) / 2 + 2,
146 u128::MAX
147 )) // 2^127 + 1 the last memory entry is `1`
148 .unwrap();
149
150 assert!(!small.is_one());
151 assert!(!large.is_one());
152 }
153}
154
155#[cfg(test)]
156mod test_is_zero {
157 use super::PolynomialRingZq;
158 use std::str::FromStr;
159
160 /// Ensure that is_zero returns `true` for the zero polynomial.
161 #[test]
162 fn zero_detection() {
163 let zero = PolynomialRingZq::from_str("0 / 2 1 1 mod 7").unwrap();
164 let zero_2 = PolynomialRingZq::from_str("2 7 14 / 2 1 1 mod 7").unwrap();
165 let zero_3 = PolynomialRingZq::from_str("3 3 3 6 / 3 1 1 2 mod 11").unwrap();
166
167 assert!(zero.is_zero());
168 assert!(zero_2.is_zero());
169 assert!(zero_3.is_zero());
170 }
171
172 /// Ensure that is_zero returns `false` for non-zero polynomials.
173 #[test]
174 fn zero_rejection() {
175 let small = PolynomialRingZq::from_str("4 0 0 0 1 / 2 1 1 mod 7").unwrap();
176 let large = PolynomialRingZq::from_str(&format!(
177 "1 {} / 2 1 1 mod {}",
178 (u128::MAX - 1) / 2 + 1,
179 u128::MAX
180 )) // last 126 bits are 0
181 .unwrap();
182
183 assert!(!small.is_zero());
184 assert!(!large.is_zero());
185 }
186}