Skip to main content

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}