1#![allow(clippy::as_conversions)]
2use crate::ring::{rq::Rq, zq::Zq};
3
4pub const SECURITY_PARAMETER: usize = 128;
6pub const OPERATOR_NORM: f64 = 15.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: Zq, pub gamma: Zq, pub gamma_1: Zq, pub gamma_2: Zq, pub beta_prime: Zq,
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: f64,
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);
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 = Self::compute_gamma(beta, tau);
71 let gamma_1 =
72 Self::compute_gamma1(base_b1, parts_t1, multiplicity, kappa, base_b2, parts_t2);
73 let gamma_2 = Self::compute_gamma2(base_b1, parts_t1, multiplicity);
74 let beta_prime = Self::compute_beta_prime(base_b, gamma, gamma_1, gamma_2);
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: Zq::new(beta as u32),
85 gamma: Zq::new(gamma as u32),
86 gamma_1: Zq::new(gamma_1 as u32),
87 gamma_2: Zq::new(gamma_2 as u32),
88 beta_prime: Zq::new(beta_prime as u32),
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: f64) -> f64 {
101 beta / ((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(beta: f64, tau: f64) -> f64 {
142 beta * (tau).sqrt()
143 }
144
145 #[allow(clippy::too_many_arguments)]
147 fn compute_gamma1(
148 b1: usize,
149 t1: usize,
150 multiplicity: usize,
151 kappa: usize,
152 b2: usize,
153 t2: usize,
154 ) -> f64 {
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).sqrt()
162 }
163
164 fn compute_gamma2(b1: usize, t1: usize, multiplicity: usize) -> f64 {
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.sqrt()
171 }
172
173 fn compute_beta_prime(b: usize, gamma: f64, gamma1: f64, gamma2: f64) -> f64 {
175 let part_z = 2.0 * (gamma * gamma) / (b * b) as f64;
176 (part_z + gamma1 * gamma1 + gamma2 * gamma2).sqrt()
177 }
178}
179
180impl Default for EnvironmentParameters {
181 fn default() -> Self {
182 Self::new(5, 3, 65535.0, 4, 5, 5, 5, 5, 65535.0, (1u64 << 32) as usize)
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use crate::ring::zq::Zq;
189
190 use super::EnvironmentParameters;
191 use super::Rq;
192
193 #[test]
194 fn test_std_s_matches_formula() {
195 let (n, r, beta) = (12, 5, 42.0);
196 let result = EnvironmentParameters::compute_std_s(n, r, beta);
197 let expected = beta / ((r * n * Rq::DEGREE) as f64).sqrt();
198 assert_eq!(result, expected);
199 }
200
201 #[test]
202 fn test_compute_base_b_not_below_two() {
203 let b = EnvironmentParameters::compute_base_b(0.001, 1, 0.0001);
204 assert!(b >= 2);
205 }
206
207 #[test]
208 fn test_base_b_formula_and_floor() {
209 let s = 0.17;
210 let (r, tau) = (3, 12.0);
211 let result = EnvironmentParameters::compute_base_b(s, r, tau);
212 let expected = ((12.0 * r as f64 * tau).sqrt() * s).sqrt().round().max(2.0) as usize;
213 assert_eq!(result, expected, "balanced radix b");
214 }
215
216 #[test]
217 fn test_t1_rounding() {
218 let (q, b) = (257usize, 4usize); let result = EnvironmentParameters::compute_t1(q, b);
220 assert_eq!(result, 4, "t1 = round(log_q / log_b)");
221 }
222
223 #[test]
224 fn test_b1_is_ceil_root() {
225 let (q, t1) = (1usize << 20, 5usize);
226 let result = EnvironmentParameters::compute_b1(q, t1);
227 let expected = (q as f64).powf(1.0 / t1 as f64).ceil() as usize;
228 assert_eq!(result, expected);
229 }
230
231 #[test]
232 fn test_t1_and_b1_are_consistent() {
233 let q = 1usize << 23;
234 let b = 137usize;
235 let t1 = EnvironmentParameters::compute_t1(q, b);
236 let b1 = EnvironmentParameters::compute_b1(q, t1);
237 assert!(b1.pow(t1 as u32) >= q);
238 }
239
240 #[test]
241 fn test_t2_b2_pair_match() {
242 let (n, s, b) = (10usize, 0.12, 150usize);
243 let t2: usize = EnvironmentParameters::compute_t2(n, s, b);
244 let b2 = EnvironmentParameters::compute_b2(n, s, t2);
245 let tmp_ln = ((24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * s * s).ln();
247 let expected_t2 = (tmp_ln / (b as f64).ln()).round() as usize;
248 assert_eq!(t2, expected_t2, "t2 formula");
249 let lhs = (b2 as f64).powf(t2 as f64);
250 let rhs = (24.0 * n as f64 * Rq::DEGREE as f64).sqrt() * s * s;
251 assert!(lhs >= rhs - 1.0);
252 }
253
254 #[test]
255 fn test_gamma_functions() {
256 let (beta, tau) = (32.0, 49.0);
257 let gamma = EnvironmentParameters::compute_gamma(beta, tau);
258 assert_eq!(gamma, beta * tau.sqrt());
259
260 let (b1, t1, r, kappa, b2, t2) = (7, 3, 2, 4, 5, 2);
262 let manual1 = ((b1 * b1 * t1) as f64 / 12.0 * r as f64 * kappa as f64 * Rq::DEGREE as f64
263 + (b2 * b2 * t2) as f64 / 12.0 * ((r * r + r) as f64) / 2.0 * Rq::DEGREE as f64)
264 .sqrt();
265 let got1 = EnvironmentParameters::compute_gamma1(b1, t1, r, kappa, b2, t2);
266 assert_eq!(got1, manual1);
267
268 let manual2 = ((b1.pow(2) * t1) as f64 / 12.0 * ((r * r + r) as f64) / 2.0
269 * Rq::DEGREE as f64)
270 .sqrt();
271 let got2 = EnvironmentParameters::compute_gamma2(b1, t1, r);
272 assert_eq!(got2, manual2);
273 }
274
275 #[test]
276 fn beta_prime_formula() {
277 let (b, gamma, g1, g2) = (11usize, 3.3, 7.7, 2.2);
278 let expected = ((2.0 * gamma * gamma) / (b * b) as f64 + g1 * g1 + g2 * g2).sqrt();
279 let result = EnvironmentParameters::compute_beta_prime(b, gamma, g1, g2);
280 assert_eq!(expected, result);
281 }
282
283 #[test]
284 fn test_gamma_and_beta_prime_are_positive() {
285 let beta = 100.0;
286 let tau = 64.0;
287 let gamma = EnvironmentParameters::compute_gamma(beta, tau);
288 assert!(gamma > 0.0);
289 let beta_p = EnvironmentParameters::compute_beta_prime(3, gamma, 2.0, 1.0);
290 assert!(beta_p > 0.0);
291 }
292
293 #[test]
294 fn default_instance_is_consistent() {
295 let p = EnvironmentParameters::default();
296
297 assert!(p.b >= Zq::TWO && p.b_1 >= 2 && p.b_2 >= 2);
298 assert!(p.t_1 > 0 && p.t_2 > 0);
299
300 assert!((p.b_1 as u64).pow(p.t_1 as u32) >= p.log_q as u64);
301 assert!((p.b_2 as u64).pow(p.t_2 as u32) > 1);
302
303 assert!(p.gamma > Zq::ZERO && p.gamma_1 > Zq::ZERO && p.gamma_2 > Zq::ZERO);
305 assert!(p.beta_prime >= p.gamma_1.max(p.gamma_2));
306 }
307
308 #[test]
309 fn random_parameter_sets_hold_invariants() {
310 for seed in 1..=20 {
311 let rank = 2 + seed as usize % 8; let multiplicity = 1 + seed as usize % 5; let beta = 10.0 + seed as f64;
314 let tau = 32.0 + seed as f64;
315 let q = (1usize << 20) + (seed as usize * 12345);
316 let params =
317 EnvironmentParameters::new(rank, multiplicity, beta, 4, 4, 4, 3, 3, tau, q);
318
319 assert!(params.b >= Zq::TWO);
321 assert!(params.t_1 >= 1 && params.t_2 >= 1);
322 assert!((params.b_1 as u64).pow(params.t_1 as u32) >= q as u64);
323 assert!(params.gamma > Zq::ZERO);
324 assert!(params.beta_prime >= params.gamma_1.max(params.gamma_2));
325 }
326 }
327}