qfall_math/integer_mod_q/polynomial_ring_zq/sample/
discrete_gauss.rs1use crate::{
13 error::MathError,
14 integer::PolyOverZ,
15 integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
16 macros::seeded::seedable_function,
17 rational::Q,
18};
19
20impl PolynomialRingZq {
21 seedable_function!(
22 #[seeded]
33 #[optionally_seeded]
35 #[optionally_seeded]
37 #[unseeded]
50 #[seeded]
52 pub(crate) fn sample_discrete_gauss(
62 modulus: impl Into<ModulusPolynomialRingZq>,
63 center: impl Into<Q>,
64 s: impl Into<Q>,
65 seed: Option<[u8; 32]>,
66 ) -> Result<Self, MathError> {
67 let modulus = modulus.into();
68 assert!(
69 modulus.get_degree() > 0,
70 "ModulusPolynomial of degree 0 is insufficient to sample over."
71 );
72
73 let poly_z = PolyOverZ::sample_discrete_gauss_optionally_seeded(
74 modulus.get_degree() - 1,
75 center,
76 s,
77 seed,
78 )?;
79 let mut poly_ringzq = PolynomialRingZq {
80 poly: poly_z,
81 modulus,
82 };
83 poly_ringzq.reduce();
84
85 Ok(poly_ringzq)
86 }
87 );
88}
89
90#[cfg(test)]
91mod test_sample_discrete_gauss {
92 use crate::utils::sample::test_seed;
93 use crate::{
94 integer::Z,
95 integer_mod_q::{ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq},
96 rational::Q,
97 traits::{GetCoefficient, SetCoefficient},
98 };
99 use std::str::FromStr;
100
101 #[test]
105 fn availability() {
106 for seeded in [false, true] {
107 let center = Q::ZERO;
108 let s = Q::ONE;
109 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
110
111 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
112 &modulus,
113 0f32,
114 1u8,
115 seeded.then(test_seed),
116 );
117 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
118 &modulus,
119 0f64,
120 1u16,
121 seeded.then(test_seed),
122 );
123 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
124 &modulus,
125 0f32,
126 1u32,
127 seeded.then(test_seed),
128 );
129 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
130 &modulus,
131 0f64,
132 1u64,
133 seeded.then(test_seed),
134 );
135 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
136 &modulus,
137 0f32,
138 1i8,
139 seeded.then(test_seed),
140 );
141 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
142 &modulus,
143 0f32,
144 1i16,
145 seeded.then(test_seed),
146 );
147 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
148 &modulus,
149 0f32,
150 1i32,
151 seeded.then(test_seed),
152 );
153 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
154 &modulus,
155 0f64,
156 1i64,
157 seeded.then(test_seed),
158 );
159 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
160 &modulus,
161 center,
162 s,
163 seeded.then(test_seed),
164 );
165 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
166 &modulus,
167 0f32,
168 1f64,
169 seeded.then(test_seed),
170 );
171
172 if seeded {
174 let _ = PolynomialRingZq::sample_discrete_gauss_seeded(
175 &modulus,
176 0f32,
177 1u8,
178 test_seed(),
179 );
180 } else {
181 let _ = PolynomialRingZq::sample_discrete_gauss(&modulus, 0f32, 1u8);
182 }
183 }
184 }
185
186 #[test]
188 fn boundaries_kept_small() {
189 for seeded in [false, true] {
190 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
191
192 for _ in 0..32 {
193 let poly = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
194 &modulus,
195 15,
196 1,
197 seeded.then(test_seed),
198 )
199 .unwrap();
200
201 for i in 0..3 {
202 let sample: Z = poly.get_coeff(i).unwrap();
203 assert!(Z::ZERO <= sample);
204 assert!(sample < modulus.get_q());
205 }
206 }
207 }
208 }
209
210 #[test]
212 fn boundaries_kept_large() {
213 for seeded in [false, true] {
214 let modulus =
215 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX)).unwrap();
216
217 for _ in 0..256 {
218 let poly = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
219 &modulus,
220 1,
221 1,
222 seeded.then(test_seed),
223 )
224 .unwrap();
225
226 for i in 0..3 {
227 let sample: Z = poly.get_coeff(i).unwrap();
228 assert!(Z::ZERO <= sample);
229 assert!(sample < modulus.get_q());
230 }
231 }
232 }
233 }
234
235 #[test]
237 fn nr_coeffs() {
238 for seeded in [false, true] {
239 let degrees = [1, 3, 7, 15, 32, 120];
240 for degree in degrees {
241 let mut modulus = PolyOverZq::from((1, u64::MAX));
242 modulus.set_coeff(degree, 1).unwrap();
243 let modulus = ModulusPolynomialRingZq::from(&modulus);
244
245 let res = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
246 &modulus,
247 i64::MAX,
248 1,
249 seeded.then(test_seed),
250 )
251 .unwrap();
252
253 assert_eq!(
254 res.get_degree() + 1,
255 modulus.get_degree(),
256 "Could fail with negligible probability."
257 );
258 }
259 }
260 }
261
262 #[test]
264 fn invalid_modulus() {
265 for seeded in [false, true] {
266 let result = std::panic::catch_unwind(|| {
267 let modulus = ModulusPolynomialRingZq::from_str("1 1 mod 17").unwrap();
268
269 let _ = PolynomialRingZq::sample_discrete_gauss_optionally_seeded(
270 &modulus,
271 0,
272 1,
273 seeded.then(test_seed),
274 )
275 .unwrap();
276 });
277 assert!(result.is_err());
278 }
279 }
280
281 #[test]
283 fn same_seed_same_sample() {
284 use crate::integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq};
285 use std::str::FromStr;
286 let modulus = ModulusPolynomialRingZq::from_str("3 1 2 1 mod 17").unwrap();
287 let sample_0 =
288 PolynomialRingZq::sample_discrete_gauss_seeded(&modulus, 0, 1, [42; 32]).unwrap();
289 let sample_1 =
290 PolynomialRingZq::sample_discrete_gauss_seeded(&modulus, 0, 1, [42; 32]).unwrap();
291
292 assert_eq!(sample_0, sample_1);
293 }
294
295 #[test]
297 fn known_answer() {
298 use crate::integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq};
299 use std::str::FromStr;
300 let modulus = ModulusPolynomialRingZq::from_str("3 1 2 1 mod 17").unwrap();
301 let sample =
302 PolynomialRingZq::sample_discrete_gauss_seeded(&modulus, 0, 1, [42; 32]).unwrap();
303
304 assert_eq!("1 1 / 3 1 2 1 mod 17", sample.to_string());
305 }
306}