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,
10 encryption::EncryptSkScratchSpace,
11 external_product::{
12 ExternalProduct, ExternalProductInplace, ExternalProductInplaceScratchSpace, ExternalProductScratchSpace,
13 },
14 ggsw::GGSWCiphertext,
15 glwe::{GLWECiphertext, GLWECiphertextFourier, GLWEPlaintext},
16 keys::{PublicKey, SecretKey, SecretKeyFourier},
17 keyswitch::{KeySwitch, KeySwitchInplace, KeySwitchInplaceScratchSpace, KeySwitchScratchSpace},
18 keyswitch_key::GLWEKeySwitchKey,
19 test_fft64::{grlwe::noise_grlwe_rlwe_product, rgsw::noise_rgsw_product},
20};
21
22#[test]
23fn encrypt_sk() {
24 let module: Module<FFT64> = Module::<FFT64>::new(32);
25 let log_base2k: usize = 8;
26 let log_k_ct: usize = 54;
27 let log_k_pt: usize = 30;
28
29 let sigma: f64 = 3.2;
30 let bound: f64 = sigma * 6.0;
31
32 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_ct);
33 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_pt);
34
35 let mut source_xs: Source = Source::new([0u8; 32]);
36 let mut source_xe: Source = Source::new([0u8; 32]);
37 let mut source_xa: Source = Source::new([0u8; 32]);
38
39 let mut scratch: ScratchOwned = ScratchOwned::new(
40 GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size()) | GLWECiphertext::decrypt_scratch_space(&module, ct.size()),
41 );
42
43 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
44 sk.fill_ternary_prob(0.5, &mut source_xs);
45
46 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
47 sk_dft.dft(&module, &sk);
48
49 let mut data_want: Vec<i64> = vec![0i64; module.n()];
50
51 data_want
52 .iter_mut()
53 .for_each(|x| *x = source_xa.next_i64() & 0xFF);
54
55 pt.data
56 .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
57
58 ct.encrypt_sk(
59 &module,
60 &pt,
61 &sk_dft,
62 &mut source_xa,
63 &mut source_xe,
64 sigma,
65 bound,
66 scratch.borrow(),
67 );
68
69 pt.data.zero();
70
71 ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
72
73 let mut data_have: Vec<i64> = vec![0i64; module.n()];
74
75 pt.data
76 .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
77
78 // TODO: properly assert the decryption noise through std(dec(ct) - pt)
79 let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
80 izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
81 let b_scaled = (*b as f64) / scale;
82 assert!(
83 (*a as f64 - b_scaled).abs() < 0.1,
84 "{} {}",
85 *a as f64,
86 b_scaled
87 )
88 });
89}
90
91#[test]
92fn encrypt_zero_sk() {
93 let module: Module<FFT64> = Module::<FFT64>::new(1024);
94 let log_base2k: usize = 8;
95 let log_k_ct: usize = 55;
96
97 let sigma: f64 = 3.2;
98 let bound: f64 = sigma * 6.0;
99
100 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_ct);
101
102 let mut source_xs: Source = Source::new([0u8; 32]);
103 let mut source_xe: Source = Source::new([1u8; 32]);
104 let mut source_xa: Source = Source::new([0u8; 32]);
105
106 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
107 sk.fill_ternary_prob(0.5, &mut source_xs);
108 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
109 sk_dft.dft(&module, &sk);
110
111 let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, log_base2k, log_k_ct);
112
113 let mut scratch: ScratchOwned = ScratchOwned::new(
114 GLWECiphertextFourier::decrypt_scratch_space(&module, ct_dft.size())
115 | GLWECiphertextFourier::encrypt_zero_sk_scratch_space(&module, ct_dft.size()),
116 );
117
118 ct_dft.encrypt_zero_sk(
119 &module,
120 &sk_dft,
121 &mut source_xa,
122 &mut source_xe,
123 sigma,
124 bound,
125 scratch.borrow(),
126 );
127 ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
128
129 assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
130}
131
132#[test]
133fn 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: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_ct);
143 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::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: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::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 GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
167 | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
168 | GLWECiphertext::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 &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: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::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
201#[test]
202fn keyswitch() {
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: GLWEKeySwitchKey<Vec<u8>, FFT64> = GLWEKeySwitchKey::new(&module, log_base2k, log_k_grlwe, rows);
214 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe_in);
215 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe_out);
216 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe_in);
217 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::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 GLWEKeySwitchKey::encrypt_sk_scratch_space(&module, ct_grlwe.size())
230 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
231 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
232 | GLWECiphertext::keyswitch_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: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::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: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::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 &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.keyswitch(&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
301#[test]
302fn keyswich_inplace() {
303 let module: Module<FFT64> = Module::<FFT64>::new(2048);
304 let log_base2k: usize = 12;
305 let log_k_grlwe: usize = 60;
306 let log_k_rlwe: usize = 45;
307 let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
308
309 let sigma: f64 = 3.2;
310 let bound: f64 = sigma * 6.0;
311
312 let mut ct_grlwe: GLWEKeySwitchKey<Vec<u8>, FFT64> = GLWEKeySwitchKey::new(&module, log_base2k, log_k_grlwe, rows);
313 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe);
314 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe);
315 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe);
316
317 let mut source_xs: Source = Source::new([0u8; 32]);
318 let mut source_xe: Source = Source::new([0u8; 32]);
319 let mut source_xa: Source = Source::new([0u8; 32]);
320
321 // Random input plaintext
322 pt_want
323 .data
324 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
325
326 let mut scratch: ScratchOwned = ScratchOwned::new(
327 GLWEKeySwitchKey::encrypt_sk_scratch_space(&module, ct_grlwe.size())
328 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
329 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
330 | GLWECiphertext::keyswitch_inplace_scratch_space(&module, ct_rlwe.size(), ct_grlwe.size()),
331 );
332
333 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
334 sk0.fill_ternary_prob(0.5, &mut source_xs);
335
336 let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
337 sk0_dft.dft(&module, &sk0);
338
339 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
340 sk1.fill_ternary_prob(0.5, &mut source_xs);
341
342 let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
343 sk1_dft.dft(&module, &sk1);
344
345 ct_grlwe.encrypt_sk(
346 &module,
347 &sk0.data,
348 &sk1_dft,
349 &mut source_xa,
350 &mut source_xe,
351 sigma,
352 bound,
353 scratch.borrow(),
354 );
355
356 ct_rlwe.encrypt_sk(
357 &module,
358 &pt_want,
359 &sk0_dft,
360 &mut source_xa,
361 &mut source_xe,
362 sigma,
363 bound,
364 scratch.borrow(),
365 );
366
367 ct_rlwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
368
369 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
370
371 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
372
373 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
374 let noise_want: f64 = noise_grlwe_rlwe_product(
375 module.n() as f64,
376 log_base2k,
377 0.5,
378 0.5,
379 0f64,
380 sigma * sigma,
381 0f64,
382 log_k_rlwe,
383 log_k_grlwe,
384 );
385
386 assert!(
387 (noise_have - noise_want).abs() <= 0.1,
388 "{} {}",
389 noise_have,
390 noise_want
391 );
392}
393
394#[test]
395fn external_product() {
396 let module: Module<FFT64> = Module::<FFT64>::new(2048);
397 let log_base2k: usize = 12;
398 let log_k_grlwe: usize = 60;
399 let log_k_rlwe_in: usize = 45;
400 let log_k_rlwe_out: usize = 60;
401 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
402
403 let sigma: f64 = 3.2;
404 let bound: f64 = sigma * 6.0;
405
406 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, log_base2k, log_k_grlwe, rows);
407 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe_in);
408 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe_out);
409 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
410 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe_in);
411 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe_out);
412
413 let mut source_xs: Source = Source::new([0u8; 32]);
414 let mut source_xe: Source = Source::new([0u8; 32]);
415 let mut source_xa: Source = Source::new([0u8; 32]);
416
417 // Random input plaintext
418 pt_want
419 .data
420 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
421
422 pt_want.to_mut().at_mut(0, 0)[1] = 1;
423
424 let k: usize = 1;
425
426 pt_rgsw.raw_mut()[k] = 1; // X^{k}
427
428 let mut scratch: ScratchOwned = ScratchOwned::new(
429 GGSWCiphertext::encrypt_sk_scratch_space(&module, ct_rgsw.size())
430 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
431 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
432 | GLWECiphertext::external_product_scratch_space(
433 &module,
434 ct_rlwe_out.size(),
435 ct_rlwe_in.size(),
436 ct_rgsw.size(),
437 ),
438 );
439
440 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
441 sk.fill_ternary_prob(0.5, &mut source_xs);
442
443 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
444 sk_dft.dft(&module, &sk);
445
446 ct_rgsw.encrypt_sk(
447 &module,
448 &pt_rgsw,
449 &sk_dft,
450 &mut source_xa,
451 &mut source_xe,
452 sigma,
453 bound,
454 scratch.borrow(),
455 );
456
457 ct_rlwe_in.encrypt_sk(
458 &module,
459 &pt_want,
460 &sk_dft,
461 &mut source_xa,
462 &mut source_xe,
463 sigma,
464 bound,
465 scratch.borrow(),
466 );
467
468 ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
469
470 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
471
472 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
473
474 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
475
476 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
477
478 let var_gct_err_lhs: f64 = sigma * sigma;
479 let var_gct_err_rhs: f64 = 0f64;
480
481 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
482 let var_a0_err: f64 = sigma * sigma;
483 let var_a1_err: f64 = 1f64 / 12f64;
484
485 let noise_want: f64 = noise_rgsw_product(
486 module.n() as f64,
487 log_base2k,
488 0.5,
489 var_msg,
490 var_a0_err,
491 var_a1_err,
492 var_gct_err_lhs,
493 var_gct_err_rhs,
494 log_k_rlwe_in,
495 log_k_grlwe,
496 );
497
498 assert!(
499 (noise_have - noise_want).abs() <= 0.1,
500 "{} {}",
501 noise_have,
502 noise_want
503 );
504}
505
506#[test]
507fn external_product_inplace() {
508 let module: Module<FFT64> = Module::<FFT64>::new(2048);
509 let log_base2k: usize = 12;
510 let log_k_grlwe: usize = 60;
511 let log_k_rlwe_in: usize = 45;
512 let log_k_rlwe_out: usize = 60;
513 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
514
515 let sigma: f64 = 3.2;
516 let bound: f64 = sigma * 6.0;
517
518 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, log_base2k, log_k_grlwe, rows);
519 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, log_base2k, log_k_rlwe_in);
520 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
521 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe_in);
522 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_rlwe_out);
523
524 let mut source_xs: Source = Source::new([0u8; 32]);
525 let mut source_xe: Source = Source::new([0u8; 32]);
526 let mut source_xa: Source = Source::new([0u8; 32]);
527
528 // Random input plaintext
529 pt_want
530 .data
531 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
532
533 pt_want.to_mut().at_mut(0, 0)[1] = 1;
534
535 let k: usize = 1;
536
537 pt_rgsw.raw_mut()[k] = 1; // X^{k}
538
539 let mut scratch: ScratchOwned = ScratchOwned::new(
540 GGSWCiphertext::encrypt_sk_scratch_space(&module, ct_rgsw.size())
541 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
542 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
543 | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size()),
544 );
545
546 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
547 sk.fill_ternary_prob(0.5, &mut source_xs);
548
549 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module);
550 sk_dft.dft(&module, &sk);
551
552 ct_rgsw.encrypt_sk(
553 &module,
554 &pt_rgsw,
555 &sk_dft,
556 &mut source_xa,
557 &mut source_xe,
558 sigma,
559 bound,
560 scratch.borrow(),
561 );
562
563 ct_rlwe.encrypt_sk(
564 &module,
565 &pt_want,
566 &sk_dft,
567 &mut source_xa,
568 &mut source_xe,
569 sigma,
570 bound,
571 scratch.borrow(),
572 );
573
574 ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
575
576 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
577
578 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
579
580 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
581
582 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
583
584 let var_gct_err_lhs: f64 = sigma * sigma;
585 let var_gct_err_rhs: f64 = 0f64;
586
587 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
588 let var_a0_err: f64 = sigma * sigma;
589 let var_a1_err: f64 = 1f64 / 12f64;
590
591 let noise_want: f64 = noise_rgsw_product(
592 module.n() as f64,
593 log_base2k,
594 0.5,
595 var_msg,
596 var_a0_err,
597 var_a1_err,
598 var_gct_err_lhs,
599 var_gct_err_rhs,
600 log_k_rlwe_in,
601 log_k_grlwe,
602 );
603
604 assert!(
605 (noise_have - noise_want).abs() <= 0.1,
606 "{} {}",
607 noise_have,
608 noise_want
609 );
610}