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