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