qfall_math/integer_mod_q/poly_over_zq/sample/uniform.rs
1// Copyright © 2023 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 according to the uniform distribution.
10
11use crate::{
12 error::MathError,
13 integer::Z,
14 integer_mod_q::{Modulus, PolyOverZq},
15 traits::SetCoefficient,
16 utils::{index::evaluate_index, sample::uniform::UniformIntegerSampler},
17};
18use std::fmt::Display;
19
20impl PolyOverZq {
21 /// Generates a [`PolyOverZq`] instance with maximum degree `max_degree`
22 /// and coefficients chosen uniform at random in `[0, modulus)`.
23 ///
24 /// The internally used uniform at random chosen bytes are generated
25 /// by [`ThreadRng`](rand::rngs::ThreadRng), which uses ChaCha12 and
26 /// is considered cryptographically secure.
27 ///
28 /// Parameters:
29 /// - `max_degree`: specifies the length of the polynomial,
30 /// i.e. the number of coefficients
31 /// - `modulus`: specifies the modulus of the coefficients and thus,
32 /// the interval size over which is sampled
33 ///
34 /// Returns a fresh [`PolyOverZq`] instance of length `max_degree` with coefficients
35 /// chosen uniform at random in `[0, modulus)` or a [`MathError`]
36 /// if the `max_degree` was smaller than `0` or the provided `modulus` was chosen too small.
37 ///
38 /// # Examples
39 /// ```
40 /// use qfall_math::integer_mod_q::PolyOverZq;
41 ///
42 /// let sample = PolyOverZq::sample_uniform(3, 17).unwrap();
43 /// ```
44 ///
45 /// # Errors and Failures
46 /// - Returns a [`MathError`] of type [`InvalidInterval`](MathError::InvalidInterval)
47 /// if the given `modulus` isn't larger than `1`, i.e. the interval size is at most `1`.
48 /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) if
49 /// the `max_degree` is negative or it does not fit into an [`i64`].
50 ///
51 /// # Panics ...
52 /// - if `modulus` is smaller than `2`.
53 pub fn sample_uniform(
54 max_degree: impl TryInto<i64> + Display + Copy,
55 modulus: impl Into<Z>,
56 ) -> Result<Self, MathError> {
57 let max_degree = evaluate_index(max_degree)?;
58 let interval_size = modulus.into();
59 let modulus = Modulus::from(&interval_size);
60 let mut poly_zq = PolyOverZq::from(&modulus);
61
62 let mut uis = UniformIntegerSampler::init(&interval_size)?;
63
64 for index in 0..=max_degree {
65 let sample = uis.sample();
66 unsafe { poly_zq.set_coeff_unchecked(index, sample) };
67 }
68 Ok(poly_zq)
69 }
70}
71
72#[cfg(test)]
73mod test_sample_uniform {
74 use crate::{
75 integer::Z,
76 integer_mod_q::{Modulus, PolyOverZq},
77 traits::GetCoefficient,
78 };
79
80 /// Checks whether the boundaries of the interval are kept for small moduli.
81 #[test]
82 fn boundaries_kept_small() {
83 let modulus = Z::from(17);
84
85 let poly_zq = PolyOverZq::sample_uniform(32, &modulus).unwrap();
86
87 for i in 0..32 {
88 let sample: Z = poly_zq.get_coeff(i).unwrap();
89 assert!(Z::ZERO <= sample);
90 assert!(sample < modulus);
91 }
92 }
93
94 /// Checks whether the boundaries of the interval are kept for large moduli.
95 #[test]
96 fn boundaries_kept_large() {
97 let modulus = Z::from(i64::MAX);
98
99 let poly_zq = PolyOverZq::sample_uniform(256, &modulus).unwrap();
100
101 for i in 0..256 {
102 let sample: Z = poly_zq.get_coeff(i).unwrap();
103 assert!(Z::ZERO <= sample);
104 assert!(sample < modulus);
105 }
106 }
107
108 /// Checks whether the number of coefficients is correct.
109 #[test]
110 fn nr_coeffs() {
111 let degrees = [1, 3, 7, 15, 32, 120];
112 for degree in degrees {
113 let res = PolyOverZq::sample_uniform(degree, u64::MAX).unwrap();
114
115 assert_eq!(
116 degree,
117 res.get_degree(),
118 "Could fail with probability 1/{}.",
119 u64::MAX
120 );
121 }
122 }
123
124 /// Checks whether providing an invalid interval/ modulus results in an error.
125 #[test]
126 #[should_panic]
127 fn invalid_modulus_negative() {
128 let _ = PolyOverZq::sample_uniform(1, i64::MIN);
129 }
130
131 /// Checks whether providing an invalid interval/ modulus results in an error.
132 #[test]
133 #[should_panic]
134 fn invalid_modulus_one() {
135 let _ = PolyOverZq::sample_uniform(1, 1);
136 }
137
138 /// Checks whether providing a length smaller than `1` results in an error.
139 #[test]
140 fn invalid_max_degree() {
141 let modulus = Z::from(15);
142
143 let res_0 = PolyOverZq::sample_uniform(-1, &modulus);
144 let res_1 = PolyOverZq::sample_uniform(i64::MIN, &modulus);
145
146 assert!(res_0.is_err());
147 assert!(res_1.is_err());
148 }
149
150 /// Checks whether `sample_uniform` is available for all types
151 /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
152 #[test]
153 fn availability() {
154 let modulus = Modulus::from(10);
155 let z = Z::from(10);
156
157 let _ = PolyOverZq::sample_uniform(1u64, 10u16).unwrap();
158 let _ = PolyOverZq::sample_uniform(1i64, 10u32).unwrap();
159 let _ = PolyOverZq::sample_uniform(1u8, 10u64).unwrap();
160 let _ = PolyOverZq::sample_uniform(1u16, 10i8).unwrap();
161 let _ = PolyOverZq::sample_uniform(1u32, 10i16).unwrap();
162 let _ = PolyOverZq::sample_uniform(1i32, 10i32).unwrap();
163 let _ = PolyOverZq::sample_uniform(1i16, 10i64).unwrap();
164 let _ = PolyOverZq::sample_uniform(1i8, &z).unwrap();
165 let _ = PolyOverZq::sample_uniform(1, z).unwrap();
166 let _ = PolyOverZq::sample_uniform(1, &modulus).unwrap();
167 let _ = PolyOverZq::sample_uniform(1, modulus).unwrap();
168 }
169}