1#![allow(clippy::as_conversions)]
2use crate::ring::{rq::Rq, zq::Zq};
3
4pub const SECURITY_PARAMETER: usize = 128;
6pub const OPERATOR_NORM: f64 = 71.0;
7
8#[derive(Clone)]
10pub struct EnvironmentParameters {
11 pub rank: usize, pub multiplicity: usize, pub b: Zq, pub b_1: usize, pub t_1: usize, pub b_2: usize, pub t_2: usize, pub beta_sq: u128, pub gamma_sq: u128, pub gamma_1_sq: u128, pub gamma_2_sq: u128, pub beta_prime_sq: u128,
28
29 pub kappa: usize, pub kappa_1: usize, pub kappa_2: usize, pub constraint_k: usize, pub constraint_l: usize, pub const_agg_length: usize, pub log_q: usize, }
42
43impl EnvironmentParameters {
44 #[allow(clippy::too_many_arguments)]
45 pub fn new(
46 rank: usize,
47 multiplicity: usize,
48 beta_sq: u128,
49 kappa: usize,
50 kappa_1: usize,
51 kappa_2: usize,
52 constraint_k: usize,
53 constraint_l: usize,
54 tau: f64,
55 modulo_q: usize,
56 ) -> Self {
57 let std_s = Self::compute_std_s(rank, multiplicity, beta_sq);
58
59 let base_b = Self::compute_base_b(std_s, multiplicity, tau);
61
62 let parts_t1 = Self::compute_t1(modulo_q, base_b);
64 let base_b1 = Self::compute_b1(modulo_q, parts_t1);
65
66 let parts_t2 = Self::compute_t2(rank, std_s, base_b);
68 let base_b2 = Self::compute_b2(rank, std_s, parts_t2);
69
70 let gamma_sq = Self::compute_gamma_sq(beta_sq, tau);
71 let gamma_1_sq =
72 Self::compute_gamma1_sq(base_b1, parts_t1, multiplicity, kappa, base_b2, parts_t2);
73 let gamma_2_sq = Self::compute_gamma2_sq(base_b1, parts_t1, multiplicity);
74 let beta_prime_sq = Self::compute_beta_prime_sq(base_b, gamma_sq, gamma_1_sq, gamma_2_sq);
75 let log_q = (modulo_q as f64).log2() as usize;
76 EnvironmentParameters {
77 rank,
78 multiplicity,
79 b: Zq::new(base_b as u32),
80 b_1: base_b1,
81 t_1: parts_t1,
82 b_2: base_b2,
83 t_2: parts_t2,
84 beta_sq,
85 gamma_sq,
86 gamma_1_sq,
87 gamma_2_sq,
88 beta_prime_sq,
89 kappa,
90 kappa_1,
91 kappa_2,
92 constraint_k,
93 constraint_l,
94 const_agg_length: usize::div_ceil(SECURITY_PARAMETER, log_q),
95 log_q, }
97 }
98
99 fn compute_std_s(n: usize, r: usize, beta_sq: u128) -> f64 {
101 ((beta_sq as f64) / ((r * n * Rq::DEGREE) as f64)).sqrt()
102 }
103
104 fn compute_base_b(s: f64, multiplicity: usize, tau: f64) -> usize {
106 let raw = ((12.0 * multiplicity as f64 * tau).sqrt() * s).sqrt();
107 let mut b = raw.round() as usize;
108 if b < 2 {
109 b = 2;
110 }
111 b
112 }
113
114 fn compute_t1(q: usize, b: usize) -> usize {
116 let log_q = (q as f64).ln();
117 let log_b = (b as f64).ln();
118 (log_q / log_b).round() as usize
119 }
120
121 fn compute_b1(q: usize, t1: usize) -> usize {
123 let root = (q as f64).powf(1.0 / t1 as f64);
124 root.ceil() as usize
125 }
126
127 fn compute_t2(n: usize, std_s: f64, b: usize) -> usize {
129 (((24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * std_s * std_s).ln() / (b as f64).ln())
130 .round() as usize
131 }
132
133 fn compute_b2(n: usize, std_s: f64, t2: usize) -> usize {
135 ((24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * std_s * std_s)
136 .powf(1.0 / t2 as f64)
137 .round() as usize
138 }
139
140 fn compute_gamma_sq(beta_sq: u128, tau: f64) -> u128 {
142 beta_sq * (tau as u128)
143 }
144
145 #[allow(clippy::too_many_arguments)]
147 fn compute_gamma1_sq(
148 b1: usize,
149 t1: usize,
150 multiplicity: usize,
151 kappa: usize,
152 b2: usize,
153 t2: usize,
154 ) -> u128 {
155 let term1 =
156 (b1.pow(2) * t1) as f64 / 12.0 * multiplicity as f64 * kappa as f64 * Rq::DEGREE as f64;
157 let term2 = (b2.pow(2) * t2) as f64 / 12.0
158 * ((multiplicity * multiplicity + multiplicity) as f64)
159 / 2.0
160 * Rq::DEGREE as f64;
161 (term1 + term2) as u128
162 }
163
164 fn compute_gamma2_sq(b1: usize, t1: usize, multiplicity: usize) -> u128 {
166 let term = (b1.pow(2) * t1) as f64 / 12.0
167 * ((multiplicity * multiplicity + multiplicity) as f64)
168 / 2.0
169 * Rq::DEGREE as f64;
170 term as u128
171 }
172
173 fn compute_beta_prime_sq(b: usize, gamma_sq: u128, gamma1_sq: u128, gamma2_sq: u128) -> u128 {
175 let part_z = 2.0 * gamma_sq as f64 / (b * b) as f64;
176 part_z as u128 + gamma1_sq + gamma2_sq
177 }
178}
179
180impl Default for EnvironmentParameters {
181 fn default() -> Self {
182 Self::new(
183 5,
184 3,
185 65535 * 65535,
186 4,
187 5,
188 5,
189 5,
190 5,
191 OPERATOR_NORM,
192 (1u64 << 32) as usize,
193 )
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use crate::ring::zq::Zq;
200
201 use super::EnvironmentParameters;
202 use super::Rq;
203
204 #[test]
205 fn test_std_s_matches_formula() {
206 let (n, r, beta_sq) = (12, 5, 42 * 42);
207 let result = EnvironmentParameters::compute_std_s(n, r, beta_sq);
208 let expected = (beta_sq as f64 / ((r * n * Rq::DEGREE) as f64)).sqrt();
209 assert_eq!(result, expected);
210 }
211
212 #[test]
213 fn test_compute_base_b_not_below_two() {
214 let b = EnvironmentParameters::compute_base_b(0.001, 1, 0.0001);
215 assert!(b >= 2);
216 }
217
218 #[test]
219 fn test_base_b_formula_and_floor() {
220 let s = 0.17;
221 let (r, tau) = (3, 12.0);
222 let result = EnvironmentParameters::compute_base_b(s, r, tau);
223 let expected = ((12.0 * r as f64 * tau).sqrt() * s).sqrt().round().max(2.0) as usize;
224 assert_eq!(result, expected, "balanced radix b");
225 }
226
227 #[test]
228 fn test_t1_rounding() {
229 let (q, b) = (257usize, 4usize); let result = EnvironmentParameters::compute_t1(q, b);
231 assert_eq!(result, 4, "t1 = round(log_q / log_b)");
232 }
233
234 #[test]
235 fn test_b1_is_ceil_root() {
236 let (q, t1) = (1usize << 20, 5usize);
237 let result = EnvironmentParameters::compute_b1(q, t1);
238 let expected = (q as f64).powf(1.0 / t1 as f64).ceil() as usize;
239 assert_eq!(result, expected);
240 }
241
242 #[test]
243 fn test_t1_and_b1_are_consistent() {
244 let q = 1usize << 23;
245 let b = 137usize;
246 let t1 = EnvironmentParameters::compute_t1(q, b);
247 let b1 = EnvironmentParameters::compute_b1(q, t1);
248 assert!(b1.pow(t1 as u32) >= q);
249 }
250
251 #[test]
252 fn test_t2_b2_pair_match() {
253 let (n, s, b) = (10usize, 0.12, 150usize);
254 let t2: usize = EnvironmentParameters::compute_t2(n, s, b);
255 let b2 = EnvironmentParameters::compute_b2(n, s, t2);
256 let tmp_ln = ((24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * s * s).ln();
258 let expected_t2 = (tmp_ln / (b as f64).ln()).round() as usize;
259 assert_eq!(t2, expected_t2, "t2 formula");
260 let lhs = (b2 as f64).powf(t2 as f64);
261 let rhs = (24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * s * s;
262 assert!(lhs >= rhs - 1.0);
263 }
264
265 #[test]
266 fn test_gamma_functions() {
267 let (beta_sq, tau) = (32, 49.0);
268 let gamma_sq = EnvironmentParameters::compute_gamma_sq(beta_sq, tau);
269 assert_eq!(gamma_sq, beta_sq * tau as u128);
270
271 let (b1, t1, r, kappa, b2, t2) = (7, 3, 2, 4, 5, 2);
273 let manual1 = (b1 * b1 * t1) as f64 / 12.0 * r as f64 * kappa as f64 * Rq::DEGREE as f64
274 + (b2 * b2 * t2) as f64 / 12.0 * ((r * r + r) as f64) / 2.0 * Rq::DEGREE as f64;
275 let got1 = EnvironmentParameters::compute_gamma1_sq(b1, t1, r, kappa, b2, t2);
276 assert_eq!(got1, manual1 as u128);
277
278 let manual2 =
279 (b1.pow(2) * t1) as f64 / 12.0 * ((r * r + r) as f64) / 2.0 * Rq::DEGREE as f64;
280 let got2 = EnvironmentParameters::compute_gamma2_sq(b1, t1, r);
281 assert_eq!(got2, manual2 as u128);
282 }
283
284 #[test]
285 fn beta_prime_formula() {
286 let (b, gamma_sq, g1_sq, g2_sq) = (11usize, 3 * 3, 7 * 7, 2 * 2);
287 let expected = ((2 * gamma_sq) as f64 / (b * b) as f64) as u128 + g1_sq + g2_sq;
288 let result = EnvironmentParameters::compute_beta_prime_sq(b, gamma_sq, g1_sq, g2_sq);
289 assert_eq!(expected, result);
290 }
291
292 #[test]
293 fn test_gamma_and_beta_prime_are_positive() {
294 let beta_sq = 100 * 100;
295 let tau = 64.0;
296 let gamma_sq = EnvironmentParameters::compute_gamma_sq(beta_sq, tau);
297 assert!(gamma_sq > 0);
298 let beta_p = EnvironmentParameters::compute_beta_prime_sq(3, gamma_sq, 2, 1);
299 assert!(beta_p > 0);
300 }
301
302 #[test]
303 fn default_instance_is_consistent() {
304 let p = EnvironmentParameters::default();
305
306 assert!(p.b >= Zq::TWO && p.b_1 >= 2 && p.b_2 >= 2);
307 assert!(p.t_1 > 0 && p.t_2 > 0);
308
309 assert!((p.b_1 as u64).pow(p.t_1 as u32) >= p.log_q as u64);
310 assert!((p.b_2 as u64).pow(p.t_2 as u32) > 1);
311
312 assert!(p.gamma_sq > 0 && p.gamma_1_sq > 0 && p.gamma_2_sq > 0);
314 assert!(p.beta_prime_sq >= p.gamma_1_sq.max(p.gamma_2_sq));
315 }
316
317 #[test]
318 fn random_parameter_sets_hold_invariants() {
319 for seed in 1..=20 {
320 let rank = 2 + seed as usize % 8; let multiplicity = 1 + seed as usize % 5; let beta_sq = (10 + seed) * (10 + seed);
323 let tau = 32.0 + seed as f64;
324 let q = (1usize << 20) + (seed as usize * 12345);
325 let params =
326 EnvironmentParameters::new(rank, multiplicity, beta_sq, 4, 4, 4, 3, 3, tau, q);
327
328 assert!(params.b >= Zq::TWO);
330 assert!(params.t_1 >= 1 && params.t_2 >= 1);
331 assert!((params.b_1 as u64).pow(params.t_1 as u32) >= q as u64);
332 assert!(params.gamma_sq > 0);
333 assert!(params.beta_prime_sq >= params.gamma_1_sq.max(params.gamma_2_sq));
334 }
335 }
336}