poulpy/core/src/test_fft64/rlwe.rs
core/src/test_fft64/rlwe.rsRaw
1#[cfg(test)]
2mod tests_rlwe {
3 use base2k::{
4 Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, VecZnxToMut,
5 ZnxViewMut, ZnxZero,
6 };
7 use itertools::izip;
8 use sampling::source::Source;
9
10 use crate::{
11 elem::{FromProdBy, FromProdByScratchSpace, Infos, ProdBy, ProdByScratchSpace},
12 grlwe::GRLWECt,
13 keys::{PublicKey, SecretKey, SecretKeyDft},
14 rgsw::RGSWCt,
15 rlwe::{RLWECt, RLWECtDft, RLWEPt},
16 test_fft64::{grlwe::noise_grlwe_rlwe_product, rgsw::noise_rgsw_rlwe_product},
17 };
18
19 #[test]
20 fn encrypt_sk() {
21 let module: Module<FFT64> = Module::<FFT64>::new(32);
22 let log_base2k: usize = 8;
23 let log_k_ct: usize = 54;
24 let log_k_pt: usize = 30;
25
26 let sigma: f64 = 3.2;
27 let bound: f64 = sigma * 6.0;
28
29 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct);
30 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_pt);
31
32 let mut source_xs: Source = Source::new([0u8; 32]);
33 let mut source_xe: Source = Source::new([0u8; 32]);
34 let mut source_xa: Source = Source::new([0u8; 32]);
35
36 let mut scratch: ScratchOwned = ScratchOwned::new(
37 RLWECt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECt::decrypt_scratch_space(&module, ct.size()),
38 );
39
40 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
41 sk.fill_ternary_prob(0.5, &mut source_xs);
42
43 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
44 sk_dft.dft(&module, &sk);
45
46 let mut data_want: Vec<i64> = vec![0i64; module.n()];
47
48 data_want
49 .iter_mut()
50 .for_each(|x| *x = source_xa.next_i64() & 0xFF);
51
52 pt.data
53 .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
54
55 ct.encrypt_sk(
56 &module,
57 Some(&pt),
58 &sk_dft,
59 &mut source_xa,
60 &mut source_xe,
61 sigma,
62 bound,
63 scratch.borrow(),
64 );
65
66 pt.data.zero();
67
68 ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
69
70 let mut data_have: Vec<i64> = vec![0i64; module.n()];
71
72 pt.data
73 .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
74
75 // TODO: properly assert the decryption noise through std(dec(ct) - pt)
76 let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
77 izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
78 let b_scaled = (*b as f64) / scale;
79 assert!(
80 (*a as f64 - b_scaled).abs() < 0.1,
81 "{} {}",
82 *a as f64,
83 b_scaled
84 )
85 });
86
87 module.free();
88 }
89
90 #[test]
91 fn encrypt_zero_sk() {
92 let module: Module<FFT64> = Module::<FFT64>::new(1024);
93 let log_base2k: usize = 8;
94 let log_k_ct: usize = 55;
95
96 let sigma: f64 = 3.2;
97 let bound: f64 = sigma * 6.0;
98
99 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
100
101 let mut source_xs: Source = Source::new([0u8; 32]);
102 let mut source_xe: Source = Source::new([1u8; 32]);
103 let mut source_xa: Source = Source::new([0u8; 32]);
104
105 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
106 sk.fill_ternary_prob(0.5, &mut source_xs);
107 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
108 sk_dft.dft(&module, &sk);
109
110 let mut ct_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
111
112 let mut scratch: ScratchOwned = ScratchOwned::new(
113 RLWECtDft::decrypt_scratch_space(&module, ct_dft.size())
114 | RLWECtDft::encrypt_zero_sk_scratch_space(&module, ct_dft.size()),
115 );
116
117 ct_dft.encrypt_zero_sk(
118 &module,
119 &sk_dft,
120 &mut source_xa,
121 &mut source_xe,
122 sigma,
123 bound,
124 scratch.borrow(),
125 );
126 ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
127
128 assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
129 module.free();
130 }
131
132 #[test]
133 fn encrypt_pk() {
134 let module: Module<FFT64> = Module::<FFT64>::new(32);
135 let log_base2k: usize = 8;
136 let log_k_ct: usize = 54;
137 let log_k_pk: usize = 64;
138
139 let sigma: f64 = 3.2;
140 let bound: f64 = sigma * 6.0;
141
142 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct);
143 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
144
145 let mut source_xs: Source = Source::new([0u8; 32]);
146 let mut source_xe: Source = Source::new([0u8; 32]);
147 let mut source_xa: Source = Source::new([0u8; 32]);
148 let mut source_xu: Source = Source::new([0u8; 32]);
149
150 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
151 sk.fill_ternary_prob(0.5, &mut source_xs);
152 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
153 sk_dft.dft(&module, &sk);
154
155 let mut pk: PublicKey<Vec<u8>, FFT64> = PublicKey::new(&module, log_base2k, log_k_pk);
156 pk.generate(
157 &module,
158 &sk_dft,
159 &mut source_xa,
160 &mut source_xe,
161 sigma,
162 bound,
163 );
164
165 let mut scratch: ScratchOwned = ScratchOwned::new(
166 RLWECt::encrypt_sk_scratch_space(&module, ct.size())
167 | RLWECt::decrypt_scratch_space(&module, ct.size())
168 | RLWECt::encrypt_pk_scratch_space(&module, pk.size()),
169 );
170
171 let mut data_want: Vec<i64> = vec![0i64; module.n()];
172
173 data_want
174 .iter_mut()
175 .for_each(|x| *x = source_xa.next_i64() & 0);
176
177 pt_want
178 .data
179 .encode_vec_i64(0, log_base2k, log_k_ct, &data_want, 10);
180
181 ct.encrypt_pk(
182 &module,
183 Some(&pt_want),
184 &pk,
185 &mut source_xu,
186 &mut source_xe,
187 sigma,
188 bound,
189 scratch.borrow(),
190 );
191
192 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
193
194 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
195
196 module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
197
198 assert!(((1.0f64 / 12.0).sqrt() - pt_want.data.std(0, log_base2k) * (log_k_ct as f64).exp2()).abs() < 0.2);
199
200 module.free();
201 }
202
203 #[test]
204 fn from_prod_by_grlwe() {
205 let module: Module<FFT64> = Module::<FFT64>::new(2048);
206 let log_base2k: usize = 12;
207 let log_k_grlwe: usize = 60;
208 let log_k_rlwe_in: usize = 45;
209 let log_k_rlwe_out: usize = 60;
210 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
211
212 let sigma: f64 = 3.2;
213 let bound: f64 = sigma * 6.0;
214
215 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
216 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
217 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
218 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
219 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
220
221 let mut source_xs: Source = Source::new([0u8; 32]);
222 let mut source_xe: Source = Source::new([0u8; 32]);
223 let mut source_xa: Source = Source::new([0u8; 32]);
224
225 // Random input plaintext
226 pt_want
227 .data
228 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
229
230 let mut scratch: ScratchOwned = ScratchOwned::new(
231 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
232 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
233 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
234 | RLWECt::from_prod_by_grlwe_scratch_space(
235 &module,
236 ct_rlwe_out.size(),
237 ct_rlwe_in.size(),
238 ct_grlwe.size(),
239 ),
240 );
241
242 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
243 sk0.fill_ternary_prob(0.5, &mut source_xs);
244
245 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
246 sk0_dft.dft(&module, &sk0);
247
248 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
249 sk1.fill_ternary_prob(0.5, &mut source_xs);
250
251 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
252 sk1_dft.dft(&module, &sk1);
253
254 ct_grlwe.encrypt_sk(
255 &module,
256 &sk0.data,
257 &sk1_dft,
258 &mut source_xa,
259 &mut source_xe,
260 sigma,
261 bound,
262 scratch.borrow(),
263 );
264
265 ct_rlwe_in.encrypt_sk(
266 &module,
267 Some(&pt_want),
268 &sk0_dft,
269 &mut source_xa,
270 &mut source_xe,
271 sigma,
272 bound,
273 scratch.borrow(),
274 );
275
276 ct_rlwe_out.from_prod_by_grlwe(&module, &ct_rlwe_in, &ct_grlwe, scratch.borrow());
277
278 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
279
280 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
281
282 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
283 let noise_want: f64 = noise_grlwe_rlwe_product(
284 module.n() as f64,
285 log_base2k,
286 0.5,
287 0.5,
288 0f64,
289 sigma * sigma,
290 0f64,
291 log_k_rlwe_in,
292 log_k_grlwe,
293 );
294
295 assert!(
296 (noise_have - noise_want).abs() <= 0.1,
297 "{} {}",
298 noise_have,
299 noise_want
300 );
301
302 module.free();
303 }
304
305 #[test]
306 fn prod_grlwe() {
307 let module: Module<FFT64> = Module::<FFT64>::new(2048);
308 let log_base2k: usize = 12;
309 let log_k_grlwe: usize = 60;
310 let log_k_rlwe: usize = 45;
311 let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
312
313 let sigma: f64 = 3.2;
314 let bound: f64 = sigma * 6.0;
315
316 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
317 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
318 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
319 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
320
321 let mut source_xs: Source = Source::new([0u8; 32]);
322 let mut source_xe: Source = Source::new([0u8; 32]);
323 let mut source_xa: Source = Source::new([0u8; 32]);
324
325 // Random input plaintext
326 pt_want
327 .data
328 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
329
330 let mut scratch: ScratchOwned = ScratchOwned::new(
331 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
332 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
333 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
334 | RLWECt::prod_by_grlwe_scratch_space(&module, ct_rlwe.size(), ct_grlwe.size()),
335 );
336
337 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
338 sk0.fill_ternary_prob(0.5, &mut source_xs);
339
340 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
341 sk0_dft.dft(&module, &sk0);
342
343 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
344 sk1.fill_ternary_prob(0.5, &mut source_xs);
345
346 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
347 sk1_dft.dft(&module, &sk1);
348
349 ct_grlwe.encrypt_sk(
350 &module,
351 &sk0.data,
352 &sk1_dft,
353 &mut source_xa,
354 &mut source_xe,
355 sigma,
356 bound,
357 scratch.borrow(),
358 );
359
360 ct_rlwe.encrypt_sk(
361 &module,
362 Some(&pt_want),
363 &sk0_dft,
364 &mut source_xa,
365 &mut source_xe,
366 sigma,
367 bound,
368 scratch.borrow(),
369 );
370
371 ct_rlwe.prod_by_grlwe(&module, &ct_grlwe, scratch.borrow());
372
373 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
374
375 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
376
377 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
378 let noise_want: f64 = noise_grlwe_rlwe_product(
379 module.n() as f64,
380 log_base2k,
381 0.5,
382 0.5,
383 0f64,
384 sigma * sigma,
385 0f64,
386 log_k_rlwe,
387 log_k_grlwe,
388 );
389
390 assert!(
391 (noise_have - noise_want).abs() <= 0.1,
392 "{} {}",
393 noise_have,
394 noise_want
395 );
396
397 module.free();
398 }
399
400 #[test]
401 fn from_prod_by_rgsw() {
402 let module: Module<FFT64> = Module::<FFT64>::new(2048);
403 let log_base2k: usize = 12;
404 let log_k_grlwe: usize = 60;
405 let log_k_rlwe_in: usize = 45;
406 let log_k_rlwe_out: usize = 60;
407 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
408
409 let sigma: f64 = 3.2;
410 let bound: f64 = sigma * 6.0;
411
412 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
413 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
414 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
415 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
416 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
417 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
418
419 let mut source_xs: Source = Source::new([0u8; 32]);
420 let mut source_xe: Source = Source::new([0u8; 32]);
421 let mut source_xa: Source = Source::new([0u8; 32]);
422
423 // Random input plaintext
424 pt_want
425 .data
426 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
427
428 pt_want.to_mut().at_mut(0, 0)[1] = 1;
429
430 let k: usize = 1;
431
432 pt_rgsw.raw_mut()[k] = 1; // X^{k}
433
434 let mut scratch: ScratchOwned = ScratchOwned::new(
435 RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
436 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
437 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
438 | RLWECt::from_prod_by_rgsw_scratch_space(
439 &module,
440 ct_rlwe_out.size(),
441 ct_rlwe_in.size(),
442 ct_rgsw.size(),
443 ),
444 );
445
446 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
447 sk.fill_ternary_prob(0.5, &mut source_xs);
448
449 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
450 sk_dft.dft(&module, &sk);
451
452 ct_rgsw.encrypt_sk(
453 &module,
454 &pt_rgsw,
455 &sk_dft,
456 &mut source_xa,
457 &mut source_xe,
458 sigma,
459 bound,
460 scratch.borrow(),
461 );
462
463 ct_rlwe_in.encrypt_sk(
464 &module,
465 Some(&pt_want),
466 &sk_dft,
467 &mut source_xa,
468 &mut source_xe,
469 sigma,
470 bound,
471 scratch.borrow(),
472 );
473
474 ct_rlwe_out.from_prod_by_rgsw(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
475
476 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
477
478 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
479
480 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
481
482 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
483
484 let var_gct_err_lhs: f64 = sigma * sigma;
485 let var_gct_err_rhs: f64 = 0f64;
486
487 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
488 let var_a0_err: f64 = sigma * sigma;
489 let var_a1_err: f64 = 1f64 / 12f64;
490
491 let noise_want: f64 = noise_rgsw_rlwe_product(
492 module.n() as f64,
493 log_base2k,
494 0.5,
495 var_msg,
496 var_a0_err,
497 var_a1_err,
498 var_gct_err_lhs,
499 var_gct_err_rhs,
500 log_k_rlwe_in,
501 log_k_grlwe,
502 );
503
504 assert!(
505 (noise_have - noise_want).abs() <= 0.1,
506 "{} {}",
507 noise_have,
508 noise_want
509 );
510
511 module.free();
512 }
513
514 #[test]
515 fn prod_by_rgsw() {
516 let module: Module<FFT64> = Module::<FFT64>::new(2048);
517 let log_base2k: usize = 12;
518 let log_k_grlwe: usize = 60;
519 let log_k_rlwe_in: usize = 45;
520 let log_k_rlwe_out: usize = 60;
521 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
522
523 let sigma: f64 = 3.2;
524 let bound: f64 = sigma * 6.0;
525
526 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
527 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
528 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
529 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
530 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
531
532 let mut source_xs: Source = Source::new([0u8; 32]);
533 let mut source_xe: Source = Source::new([0u8; 32]);
534 let mut source_xa: Source = Source::new([0u8; 32]);
535
536 // Random input plaintext
537 pt_want
538 .data
539 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
540
541 pt_want.to_mut().at_mut(0, 0)[1] = 1;
542
543 let k: usize = 1;
544
545 pt_rgsw.raw_mut()[k] = 1; // X^{k}
546
547 let mut scratch: ScratchOwned = ScratchOwned::new(
548 RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
549 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
550 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
551 | RLWECt::prod_by_rgsw_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size()),
552 );
553
554 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
555 sk.fill_ternary_prob(0.5, &mut source_xs);
556
557 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
558 sk_dft.dft(&module, &sk);
559
560 ct_rgsw.encrypt_sk(
561 &module,
562 &pt_rgsw,
563 &sk_dft,
564 &mut source_xa,
565 &mut source_xe,
566 sigma,
567 bound,
568 scratch.borrow(),
569 );
570
571 ct_rlwe.encrypt_sk(
572 &module,
573 Some(&pt_want),
574 &sk_dft,
575 &mut source_xa,
576 &mut source_xe,
577 sigma,
578 bound,
579 scratch.borrow(),
580 );
581
582 ct_rlwe.prod_by_rgsw(&module, &ct_rgsw, scratch.borrow());
583
584 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
585
586 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
587
588 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
589
590 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
591
592 let var_gct_err_lhs: f64 = sigma * sigma;
593 let var_gct_err_rhs: f64 = 0f64;
594
595 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
596 let var_a0_err: f64 = sigma * sigma;
597 let var_a1_err: f64 = 1f64 / 12f64;
598
599 let noise_want: f64 = noise_rgsw_rlwe_product(
600 module.n() as f64,
601 log_base2k,
602 0.5,
603 var_msg,
604 var_a0_err,
605 var_a1_err,
606 var_gct_err_lhs,
607 var_gct_err_rhs,
608 log_k_rlwe_in,
609 log_k_grlwe,
610 );
611
612 assert!(
613 (noise_have - noise_want).abs() <= 0.1,
614 "{} {}",
615 noise_have,
616 noise_want
617 );
618
619 module.free();
620 }
621}