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