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