qfall_math/integer_mod_q/mat_polynomial_ring_zq/sample/binomial.rs
1// Copyright 2023 Jan Niklas Siemer
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//! This module contains algorithms for sampling
10//! according to the binomial distribution.
11
12use crate::{
13 error::MathError,
14 integer::{MatPolyOverZ, Z},
15 integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
16 macros::seeded::seedable_function,
17 rational::Q,
18};
19use std::fmt::Display;
20
21impl MatPolynomialRingZq {
22 seedable_function!(
23 /// Outputs a [`MatPolynomialRingZq`] instance with entries chosen according to the binomial
24 /// distribution parameterized by `n` and `p`.
25 ///
26 /// Parameters:
27 /// - `num_rows`: specifies the number of rows the new matrix should have
28 /// - `num_cols`: specifies the number of columns the new matrix should have
29 /// - `modulus`: specifies the [`ModulusPolynomialRingZq`] over which the
30 /// ring of polynomials modulo `modulus.get_q()` is defined
31 /// - `n`: specifies the number of trials
32 /// - `p`: specifies the probability of success
33 #[seeded]
34 /// - `seed`: specifies the 256-bit seed of the PRNG used for sampling
35 #[optionally_seeded]
36 /// - `seed`: specifies an optional 256-bit seed of the PRNG used for sampling.
37 #[optionally_seeded]
38 /// If `None` is provided, a fresh [`ThreadRng`](rand::rngs::ThreadRng) is used instead.
39 ///
40 /// Returns a new [`MatPolynomialRingZq`] instance with entries chosen
41 /// according to the binomial distribution or a [`MathError`]
42 /// if `n < 0`, `p ∉ (0,1)`, `n` does not fit into an [`i64`],
43 /// or the dimensions of the matrix were chosen too small.
44 ///
45 /// # Examples
46 /// ```
47 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
48 /// use std::str::FromStr;
49 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
50 ///
51 #[unseeded]
52 /// let sample = MatPolynomialRingZq::sample_binomial(2, 2, &modulus, 2, 0.5).unwrap();
53 #[seeded]
54 /// let sample = MatPolynomialRingZq::sample_binomial_seeded(2, 2, &modulus, 2, 0.5, [42; 32]).unwrap();
55 /// ```
56 ///
57 /// # Errors and Failures
58 /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput)
59 /// if `n < 0` or `p ∉ (0,1)`.
60 /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
61 /// if `n` does not fit into an [`i64`].
62 ///
63 /// # Panics ...
64 /// - if the provided number of rows and columns are not suited to create a matrix.
65 /// For further information see [`MatPolynomialRingZq::new`].
66 /// - if the provided [`ModulusPolynomialRingZq`] has degree `0` or smaller.
67 pub(crate) fn sample_binomial(
68 num_rows: impl TryInto<i64> + Display,
69 num_cols: impl TryInto<i64> + Display,
70 modulus: impl Into<ModulusPolynomialRingZq>,
71 n: impl Into<Z>,
72 p: impl Into<Q>,
73 seed: Option<[u8; 32]>,
74 ) -> Result<Self, MathError> {
75 Self::sample_binomial_with_offset_optionally_seeded(
76 num_rows, num_cols, modulus, 0, n, p, seed,
77 )
78 }
79 );
80
81 seedable_function!(
82 /// Outputs a [`MatPolynomialRingZq`] instance with entries chosen according to the binomial
83 /// distribution parameterized by `n` and `p` with given `offset`.
84 ///
85 /// Parameters:
86 /// - `num_rows`: specifies the number of rows the new matrix should have
87 /// - `num_cols`: specifies the number of columns the new matrix should have
88 /// - `modulus`: specifies the [`ModulusPolynomialRingZq`] over which the
89 /// ring of polynomials modulo `modulus.get_q()` is defined
90 /// - `offset`: specifies an offset applied to each sample
91 /// collected from the binomial distribution
92 /// - `n`: specifies the number of trials
93 /// - `p`: specifies the probability of success
94 #[seeded]
95 /// - `seed`: specifies the 256-bit seed of the PRNG used for sampling
96 #[optionally_seeded]
97 /// - `seed`: specifies an optional 256-bit seed of the PRNG used for sampling.
98 #[optionally_seeded]
99 /// If `None` is provided, a fresh [`ThreadRng`](rand::rngs::ThreadRng) is used instead.
100 ///
101 /// Returns a new [`MatPolynomialRingZq`] instance with entries chosen
102 /// according to the binomial distribution or a [`MathError`]
103 /// if `n < 0`, `p ∉ (0,1)`, `n` does not fit into an [`i64`],
104 /// or the dimensions of the matrix were chosen too small.
105 ///
106 /// # Examples
107 /// ```
108 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
109 /// use std::str::FromStr;
110 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
111 ///
112 #[unseeded]
113 /// let sample = MatPolynomialRingZq::sample_binomial_with_offset(2, 2, &modulus, -1, 2, 0.5).unwrap();
114 #[seeded]
115 /// let sample = MatPolynomialRingZq::sample_binomial_with_offset_seeded(2, 2, &modulus, -1, 2, 0.5, [42; 32]).unwrap();
116 /// ```
117 ///
118 /// # Errors and Failures
119 /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput)
120 /// if `n < 0` or `p ∉ (0,1)`.
121 /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
122 /// if `n` does not fit into an [`i64`].
123 ///
124 /// # Panics ...
125 /// - if the provided number of rows and columns are not suited to create a matrix.
126 /// For further information see [`MatPolynomialRingZq::new`].
127 /// - if the provided [`ModulusPolynomialRingZq`] has degree `0`.
128 pub(crate) fn sample_binomial_with_offset(
129 num_rows: impl TryInto<i64> + Display,
130 num_cols: impl TryInto<i64> + Display,
131 modulus: impl Into<ModulusPolynomialRingZq>,
132 offset: impl Into<Z>,
133 n: impl Into<Z>,
134 p: impl Into<Q>,
135 seed: Option<[u8; 32]>,
136 ) -> Result<Self, MathError> {
137 let modulus = modulus.into();
138 assert!(
139 modulus.get_degree() > 0,
140 "ModulusPolynomial of degree 0 is insufficient to sample over."
141 );
142
143 let matrix = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
144 num_rows,
145 num_cols,
146 modulus.get_degree() - 1,
147 offset,
148 n,
149 p,
150 seed,
151 )?;
152 Ok(MatPolynomialRingZq::from((matrix, modulus)))
153 }
154 );
155}
156
157#[cfg(test)]
158mod test_sample_binomial {
159 use super::{MatPolynomialRingZq, ModulusPolynomialRingZq, Q, Z};
160 use crate::utils::sample::test_seed;
161 use crate::{
162 integer::PolyOverZ,
163 integer_mod_q::PolyOverZq,
164 traits::{GetCoefficient, MatrixDimensions, MatrixGetEntry, SetCoefficient},
165 };
166 use std::str::FromStr;
167
168 // As all major tests regarding an appropriate binomial distribution,
169 // whether the correct interval is kept, and if the errors are thrown correctly,
170 // are performed in the `utils` module, we omit these tests here.
171
172 /// Checks whether the boundaries of the interval are kept.
173 #[test]
174 fn boundaries_kept() {
175 for seeded in [false, true] {
176 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
177
178 for _ in 0..8 {
179 let matrix = MatPolynomialRingZq::sample_binomial_optionally_seeded(
180 1,
181 1,
182 &modulus,
183 2,
184 0.5,
185 seeded.then(test_seed),
186 )
187 .unwrap();
188 let entry: PolyOverZ = matrix.get_entry(0, 0).unwrap();
189 let poly = entry.get_coeff(0).unwrap();
190
191 assert!(Z::ZERO <= poly);
192 assert!(poly <= 2);
193 }
194 }
195 }
196
197 /// Checks whether the number of coefficients is correct.
198 #[test]
199 fn nr_coeffs() {
200 for seeded in [false, true] {
201 let degrees = [1, 3, 7, 15, 32, 120];
202 for degree in degrees {
203 let mut modulus = PolyOverZq::from((1, u64::MAX));
204 modulus.set_coeff(degree, 1).unwrap();
205 let modulus = ModulusPolynomialRingZq::from(&modulus);
206
207 let matrix = MatPolynomialRingZq::sample_binomial_optionally_seeded(
208 1,
209 1,
210 modulus,
211 256,
212 0.99999,
213 seeded.then(test_seed),
214 )
215 .unwrap();
216 let poly: PolyOverZ = matrix.get_entry(0, 0).unwrap();
217
218 assert_eq!(
219 degree,
220 poly.get_degree() + 1,
221 "This test can fail with probability close to 0."
222 );
223 }
224 }
225 }
226
227 /// Checks whether matrices with at least one dimension chosen smaller than `1`
228 /// or too big for an [`i64`] results in an error.
229 #[test]
230 fn false_size() {
231 for seeded in [false, true] {
232 let result = std::panic::catch_unwind(|| {
233 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
234
235 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
236 0,
237 3,
238 modulus,
239 1,
240 0.5,
241 seeded.then(test_seed),
242 );
243 });
244 assert!(result.is_err());
245 }
246 }
247
248 /// Checks whether 0 modulus polynomial is insufficient.
249 #[test]
250 fn invalid_modulus() {
251 for seeded in [false, true] {
252 let result = std::panic::catch_unwind(|| {
253 let modulus = ModulusPolynomialRingZq::from_str("1 1 mod 17").unwrap();
254
255 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
256 1,
257 1,
258 modulus,
259 1,
260 0.5,
261 seeded.then(test_seed),
262 );
263 });
264 assert!(result.is_err());
265 }
266 }
267
268 /// Checks whether `sample_binomial` is available for all types
269 /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
270 /// and [`Into<Q>`], i.e. u8, u16, i8, i16, f32, f64, ...
271 #[test]
272 fn availability() {
273 for seeded in [false, true] {
274 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
275
276 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
277 1,
278 1,
279 &modulus,
280 1u16,
281 7u8,
282 seeded.then(test_seed),
283 );
284 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
285 1,
286 1,
287 &modulus,
288 1u32,
289 7u16,
290 seeded.then(test_seed),
291 );
292 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
293 1,
294 1,
295 &modulus,
296 1u64,
297 7u32,
298 seeded.then(test_seed),
299 );
300 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
301 1,
302 1,
303 &modulus,
304 1i8,
305 7u64,
306 seeded.then(test_seed),
307 );
308 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
309 1,
310 1,
311 &modulus,
312 1i16,
313 7i8,
314 seeded.then(test_seed),
315 );
316 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
317 1,
318 1,
319 &modulus,
320 1i32,
321 7i16,
322 seeded.then(test_seed),
323 );
324 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
325 1,
326 1,
327 &modulus,
328 1i64,
329 7i32,
330 seeded.then(test_seed),
331 );
332 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
333 1,
334 1,
335 &modulus,
336 Z::ONE,
337 7i64,
338 seeded.then(test_seed),
339 );
340 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
341 1,
342 1,
343 &modulus,
344 1u8,
345 0.5f32,
346 seeded.then(test_seed),
347 );
348 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
349 1,
350 1,
351 &modulus,
352 1u8,
353 0.5f64,
354 seeded.then(test_seed),
355 );
356 let _ = MatPolynomialRingZq::sample_binomial_optionally_seeded(
357 1,
358 1,
359 &modulus,
360 1,
361 Q::from((1, 2)),
362 seeded.then(test_seed),
363 );
364
365 // explicitly checks the existence of the unseeded and the seeded function
366 if seeded {
367 let _ = MatPolynomialRingZq::sample_binomial_seeded(
368 1,
369 1,
370 &modulus,
371 1u16,
372 7u8,
373 test_seed(),
374 );
375 } else {
376 let _ = MatPolynomialRingZq::sample_binomial(1, 1, &modulus, 1u16, 7u8);
377 }
378 }
379 }
380
381 /// Checks whether the size of uniformly random sampled matrices
382 /// fits the specified dimensions.
383 #[test]
384 fn matrix_size() {
385 for seeded in [false, true] {
386 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
387
388 let mat_0 = MatPolynomialRingZq::sample_binomial_optionally_seeded(
389 3,
390 3,
391 &modulus,
392 1,
393 0.5,
394 seeded.then(test_seed),
395 )
396 .unwrap();
397 let mat_1 = MatPolynomialRingZq::sample_binomial_optionally_seeded(
398 4,
399 1,
400 &modulus,
401 1,
402 0.5,
403 seeded.then(test_seed),
404 )
405 .unwrap();
406 let mat_2 = MatPolynomialRingZq::sample_binomial_optionally_seeded(
407 1,
408 5,
409 &modulus,
410 1,
411 0.5,
412 seeded.then(test_seed),
413 )
414 .unwrap();
415 let mat_3 = MatPolynomialRingZq::sample_binomial_optionally_seeded(
416 15,
417 20,
418 &modulus,
419 1,
420 0.5,
421 seeded.then(test_seed),
422 )
423 .unwrap();
424
425 assert_eq!(3, mat_0.get_num_rows());
426 assert_eq!(3, mat_0.get_num_columns());
427 assert_eq!(4, mat_1.get_num_rows());
428 assert_eq!(1, mat_1.get_num_columns());
429 assert_eq!(1, mat_2.get_num_rows());
430 assert_eq!(5, mat_2.get_num_columns());
431 assert_eq!(15, mat_3.get_num_rows());
432 assert_eq!(20, mat_3.get_num_columns());
433 }
434 }
435
436 /// Checks whether the same seed results in the same sample.
437 #[test]
438 fn same_seed_same_sample() {
439 use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
440 use std::str::FromStr;
441 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
442 let sample_0 =
443 MatPolynomialRingZq::sample_binomial_seeded(2, 2, &modulus, 2, 0.5, [42; 32]).unwrap();
444 let sample_1 =
445 MatPolynomialRingZq::sample_binomial_seeded(2, 2, &modulus, 2, 0.5, [42; 32]).unwrap();
446
447 assert_eq!(sample_0, sample_1);
448 }
449
450 /// Checks whether the seeded variant outputs the known sample for a fixed seed.
451 #[test]
452 fn known_answer() {
453 use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
454 use std::str::FromStr;
455 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
456 let sample =
457 MatPolynomialRingZq::sample_binomial_seeded(2, 2, &modulus, 2, 0.5, [42; 32]).unwrap();
458
459 assert_eq!(
460 "[[3 0 2 1, 3 2 0 1],[2 0 1, 3 1 0 1]] / 4 1 0 0 1 mod 17",
461 sample.to_string()
462 );
463 }
464}
465
466#[cfg(test)]
467mod test_sample_binomial_with_offset {
468 use super::{MatPolynomialRingZq, ModulusPolynomialRingZq, Q, Z};
469 use crate::utils::sample::test_seed;
470 use crate::{
471 integer::PolyOverZ,
472 integer_mod_q::PolyOverZq,
473 traits::{GetCoefficient, MatrixDimensions, MatrixGetEntry, SetCoefficient},
474 };
475 use std::str::FromStr;
476
477 // As all major tests regarding an appropriate binomial distribution,
478 // whether the correct interval is kept, and if the errors are thrown correctly,
479 // are performed in the `utils` module, we omit these tests here.
480
481 /// Checks whether the boundaries of the interval are kept.
482 #[test]
483 fn boundaries_kept() {
484 for seeded in [false, true] {
485 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
486
487 for _ in 0..8 {
488 let matrix = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
489 1,
490 1,
491 &modulus,
492 -1,
493 2,
494 0.5,
495 seeded.then(test_seed),
496 )
497 .unwrap();
498 let poly: PolyOverZ = matrix.get_entry(0, 0).unwrap();
499 let value = poly.get_coeff(0).unwrap();
500
501 assert!(16 <= value || value <= Z::ONE);
502 }
503 }
504 }
505
506 /// Checks whether the number of coefficients is correct.
507 #[test]
508 fn nr_coeffs() {
509 for seeded in [false, true] {
510 let degrees = [1, 3, 7, 15, 32, 120];
511 for degree in degrees {
512 let mut modulus = PolyOverZq::from((1, u64::MAX));
513 modulus.set_coeff(degree, 1).unwrap();
514 let modulus = ModulusPolynomialRingZq::from(&modulus);
515
516 let matrix = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
517 1,
518 1,
519 modulus,
520 1,
521 256,
522 0.99999,
523 seeded.then(test_seed),
524 )
525 .unwrap();
526 let poly: PolyOverZ = matrix.get_entry(0, 0).unwrap();
527
528 assert_eq!(degree, poly.get_degree() + 1,);
529 }
530 }
531 }
532
533 /// Checks whether matrices with at least one dimension chosen smaller than `1`
534 /// or too big for an [`i64`] results in an error.
535 #[test]
536 fn false_size() {
537 for seeded in [false, true] {
538 let result = std::panic::catch_unwind(|| {
539 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
540
541 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
542 0,
543 3,
544 modulus,
545 -1,
546 1,
547 0.5,
548 seeded.then(test_seed),
549 );
550 });
551 assert!(result.is_err());
552 }
553 }
554
555 /// Checks whether 0 modulus polynomial is insufficient.
556 #[test]
557 fn invalid_modulus() {
558 for seeded in [false, true] {
559 let result = std::panic::catch_unwind(|| {
560 let modulus = ModulusPolynomialRingZq::from_str("1 1 mod 17").unwrap();
561
562 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
563 1,
564 1,
565 modulus,
566 -1,
567 1,
568 0.5,
569 seeded.then(test_seed),
570 );
571 });
572 assert!(result.is_err());
573 }
574 }
575
576 /// Checks whether `sample_binomial_with_offset` is available for all types
577 /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
578 /// and [`Into<Q>`], i.e. u8, u16, i8, i16, f32, f64, ...
579 #[test]
580 fn availability() {
581 for seeded in [false, true] {
582 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
583
584 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
585 1,
586 1,
587 &modulus,
588 -1,
589 1u16,
590 7u8,
591 seeded.then(test_seed),
592 );
593 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
594 1,
595 1,
596 &modulus,
597 0,
598 1u32,
599 7u16,
600 seeded.then(test_seed),
601 );
602 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
603 1,
604 1,
605 &modulus,
606 -1,
607 1u64,
608 7u32,
609 seeded.then(test_seed),
610 );
611 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
612 1,
613 1,
614 &modulus,
615 -1,
616 1i8,
617 7u64,
618 seeded.then(test_seed),
619 );
620 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
621 1,
622 1,
623 &modulus,
624 -1,
625 1i16,
626 7i8,
627 seeded.then(test_seed),
628 );
629 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
630 1,
631 1,
632 &modulus,
633 -1,
634 1i32,
635 7i16,
636 seeded.then(test_seed),
637 );
638 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
639 1,
640 1,
641 &modulus,
642 -1,
643 1i64,
644 7i32,
645 seeded.then(test_seed),
646 );
647 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
648 1,
649 1,
650 &modulus,
651 -1,
652 Z::ONE,
653 7i64,
654 seeded.then(test_seed),
655 );
656 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
657 1,
658 1,
659 &modulus,
660 -1,
661 1u8,
662 0.5f32,
663 seeded.then(test_seed),
664 );
665 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
666 1,
667 1,
668 &modulus,
669 1,
670 1u8,
671 0.5f64,
672 seeded.then(test_seed),
673 );
674 let _ = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
675 1,
676 1,
677 &modulus,
678 Z::ONE,
679 1,
680 Q::from((1, 2)),
681 seeded.then(test_seed),
682 );
683
684 // explicitly checks the existence of the unseeded and the seeded function
685 if seeded {
686 let _ = MatPolynomialRingZq::sample_binomial_with_offset_seeded(
687 1,
688 1,
689 &modulus,
690 -1,
691 1u16,
692 7u8,
693 test_seed(),
694 );
695 } else {
696 let _ =
697 MatPolynomialRingZq::sample_binomial_with_offset(1, 1, &modulus, -1, 1u16, 7u8);
698 }
699 }
700 }
701
702 /// Checks whether the size of uniformly random sampled matrices
703 /// fits the specified dimensions.
704 #[test]
705 fn matrix_size() {
706 for seeded in [false, true] {
707 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
708
709 let mat_0 = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
710 3,
711 3,
712 &modulus,
713 -1,
714 1,
715 0.5,
716 seeded.then(test_seed),
717 )
718 .unwrap();
719 let mat_1 = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
720 4,
721 1,
722 &modulus,
723 -1,
724 1,
725 0.5,
726 seeded.then(test_seed),
727 )
728 .unwrap();
729 let mat_2 = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
730 1,
731 5,
732 &modulus,
733 -1,
734 1,
735 0.5,
736 seeded.then(test_seed),
737 )
738 .unwrap();
739 let mat_3 = MatPolynomialRingZq::sample_binomial_with_offset_optionally_seeded(
740 15,
741 20,
742 &modulus,
743 -1,
744 1,
745 0.5,
746 seeded.then(test_seed),
747 )
748 .unwrap();
749
750 assert_eq!(3, mat_0.get_num_rows());
751 assert_eq!(3, mat_0.get_num_columns());
752 assert_eq!(4, mat_1.get_num_rows());
753 assert_eq!(1, mat_1.get_num_columns());
754 assert_eq!(1, mat_2.get_num_rows());
755 assert_eq!(5, mat_2.get_num_columns());
756 assert_eq!(15, mat_3.get_num_rows());
757 assert_eq!(20, mat_3.get_num_columns());
758 }
759 }
760
761 /// Checks whether the same seed results in the same sample.
762 #[test]
763 fn same_seed_same_sample() {
764 use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
765 use std::str::FromStr;
766 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
767 let sample_0 = MatPolynomialRingZq::sample_binomial_with_offset_seeded(
768 2, 2, &modulus, -1, 2, 0.5, [42; 32],
769 )
770 .unwrap();
771 let sample_1 = MatPolynomialRingZq::sample_binomial_with_offset_seeded(
772 2, 2, &modulus, -1, 2, 0.5, [42; 32],
773 )
774 .unwrap();
775
776 assert_eq!(sample_0, sample_1);
777 }
778
779 /// Checks whether the seeded variant outputs the known sample for a fixed seed.
780 #[test]
781 fn known_answer() {
782 use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
783 use std::str::FromStr;
784 let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
785 let sample = MatPolynomialRingZq::sample_binomial_with_offset_seeded(
786 2, 2, &modulus, -1, 2, 0.5, [42; 32],
787 )
788 .unwrap();
789
790 assert_eq!(
791 "[[2 16 1, 2 1 16],[3 16 0 16, 2 0 16]] / 4 1 0 0 1 mod 17",
792 sample.to_string()
793 );
794 }
795}