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() {
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 let module: Module<FFT64> = Module::<FFT64>::new(2048);
201 let basek: usize = 12;
202 let log_k_grlwe: usize = 60;
203 let log_k_rlwe_in: usize = 45;
204 let log_k_rlwe_out: usize = 60;
205 let rows: usize = (log_k_rlwe_in + basek - 1) / basek;
206 let rank: usize = 1;
207
208 let sigma: f64 = 3.2;
209
210 let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::new(&module, basek, log_k_grlwe, rows, rank, rank);
211 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe_in, rank);
212 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe_out, rank);
213 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_in);
214 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_out);
215
216 let mut source_xs: Source = Source::new([0u8; 32]);
217 let mut source_xe: Source = Source::new([0u8; 32]);
218 let mut source_xa: Source = Source::new([0u8; 32]);
219
220 // Random input plaintext
221 pt_want
222 .data
223 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
224
225 let mut scratch: ScratchOwned = ScratchOwned::new(
226 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe.size())
227 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
228 | GLWECiphertext::encrypt_sk_scratch_space(&module, rank, ct_rlwe_in.size())
229 | GLWECiphertext::keyswitch_scratch_space(
230 &module,
231 ct_rlwe_out.size(),
232 ct_rlwe_in.size(),
233 ct_grlwe.size(),
234 ),
235 );
236
237 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
238 sk0.fill_ternary_prob(0.5, &mut source_xs);
239
240 let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
241 sk0_dft.dft(&module, &sk0);
242
243 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
244 sk1.fill_ternary_prob(0.5, &mut source_xs);
245
246 let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
247 sk1_dft.dft(&module, &sk1);
248
249 ct_grlwe.encrypt_sk(
250 &module,
251 &sk0.data,
252 &sk1_dft,
253 &mut source_xa,
254 &mut source_xe,
255 sigma,
256 scratch.borrow(),
257 );
258
259 ct_rlwe_in.encrypt_sk(
260 &module,
261 &pt_want,
262 &sk0_dft,
263 &mut source_xa,
264 &mut source_xe,
265 sigma,
266 scratch.borrow(),
267 );
268
269 ct_rlwe_out.keyswitch(&module, &ct_rlwe_in, &ct_grlwe, scratch.borrow());
270
271 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
272
273 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
274
275 let noise_have: f64 = pt_have.data.std(0, basek).log2();
276 let noise_want: f64 = noise_grlwe_rlwe_product(
277 module.n() as f64,
278 basek,
279 0.5,
280 0.5,
281 0f64,
282 sigma * sigma,
283 0f64,
284 log_k_rlwe_in,
285 log_k_grlwe,
286 );
287
288 assert!(
289 (noise_have - noise_want).abs() <= 0.1,
290 "{} {}",
291 noise_have,
292 noise_want
293 );
294}
295
296#[test]
297fn keyswich_inplace() {
298 let module: Module<FFT64> = Module::<FFT64>::new(2048);
299 let basek: usize = 12;
300 let log_k_grlwe: usize = 60;
301 let log_k_rlwe: usize = 45;
302 let rows: usize = (log_k_rlwe + basek - 1) / basek;
303 let rank: usize = 1;
304
305 let sigma: f64 = 3.2;
306
307 let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::new(&module, basek, log_k_grlwe, rows, rank, rank);
308 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe, rank);
309 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe);
310 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe);
311
312 let mut source_xs: Source = Source::new([0u8; 32]);
313 let mut source_xe: Source = Source::new([0u8; 32]);
314 let mut source_xa: Source = Source::new([0u8; 32]);
315
316 // Random input plaintext
317 pt_want
318 .data
319 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
320
321 let mut scratch: ScratchOwned = ScratchOwned::new(
322 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe.size())
323 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
324 | GLWECiphertext::encrypt_sk_scratch_space(&module, rank, ct_rlwe.size())
325 | GLWECiphertext::keyswitch_inplace_scratch_space(&module, ct_rlwe.size(), ct_grlwe.size()),
326 );
327
328 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
329 sk0.fill_ternary_prob(0.5, &mut source_xs);
330
331 let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
332 sk0_dft.dft(&module, &sk0);
333
334 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
335 sk1.fill_ternary_prob(0.5, &mut source_xs);
336
337 let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
338 sk1_dft.dft(&module, &sk1);
339
340 ct_grlwe.encrypt_sk(
341 &module,
342 &sk0.data,
343 &sk1_dft,
344 &mut source_xa,
345 &mut source_xe,
346 sigma,
347 scratch.borrow(),
348 );
349
350 ct_rlwe.encrypt_sk(
351 &module,
352 &pt_want,
353 &sk0_dft,
354 &mut source_xa,
355 &mut source_xe,
356 sigma,
357 scratch.borrow(),
358 );
359
360 ct_rlwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
361
362 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
363
364 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
365
366 let noise_have: f64 = pt_have.data.std(0, basek).log2();
367 let noise_want: f64 = noise_grlwe_rlwe_product(
368 module.n() as f64,
369 basek,
370 0.5,
371 0.5,
372 0f64,
373 sigma * sigma,
374 0f64,
375 log_k_rlwe,
376 log_k_grlwe,
377 );
378
379 assert!(
380 (noise_have - noise_want).abs() <= 0.1,
381 "{} {}",
382 noise_have,
383 noise_want
384 );
385}
386
387#[test]
388fn external_product() {
389 let module: Module<FFT64> = Module::<FFT64>::new(2048);
390 let basek: usize = 12;
391 let log_k_grlwe: usize = 60;
392 let log_k_rlwe_in: usize = 45;
393 let log_k_rlwe_out: usize = 60;
394 let rows: usize = (log_k_rlwe_in + basek - 1) / basek;
395 let rank: usize = 1;
396
397 let sigma: f64 = 3.2;
398
399 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, log_k_grlwe, rows, rank);
400 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe_in, rank);
401 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe_out, rank);
402 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
403 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_in);
404 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_out);
405
406 let mut source_xs: Source = Source::new([0u8; 32]);
407 let mut source_xe: Source = Source::new([0u8; 32]);
408 let mut source_xa: Source = Source::new([0u8; 32]);
409
410 // Random input plaintext
411 pt_want
412 .data
413 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
414
415 pt_want.to_mut().at_mut(0, 0)[1] = 1;
416
417 let k: usize = 1;
418
419 pt_rgsw.raw_mut()[k] = 1; // X^{k}
420
421 let mut scratch: ScratchOwned = ScratchOwned::new(
422 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
423 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
424 | GLWECiphertext::encrypt_sk_scratch_space(&module, rank, ct_rlwe_in.size())
425 | GLWECiphertext::external_product_scratch_space(
426 &module,
427 ct_rlwe_out.size(),
428 ct_rlwe_in.size(),
429 ct_rgsw.size(),
430 ),
431 );
432
433 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
434 sk.fill_ternary_prob(0.5, &mut source_xs);
435
436 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
437 sk_dft.dft(&module, &sk);
438
439 ct_rgsw.encrypt_sk(
440 &module,
441 &pt_rgsw,
442 &sk_dft,
443 &mut source_xa,
444 &mut source_xe,
445 sigma,
446 scratch.borrow(),
447 );
448
449 ct_rlwe_in.encrypt_sk(
450 &module,
451 &pt_want,
452 &sk_dft,
453 &mut source_xa,
454 &mut source_xe,
455 sigma,
456 scratch.borrow(),
457 );
458
459 ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
460
461 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
462
463 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
464
465 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
466
467 let noise_have: f64 = pt_have.data.std(0, basek).log2();
468
469 let var_gct_err_lhs: f64 = sigma * sigma;
470 let var_gct_err_rhs: f64 = 0f64;
471
472 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
473 let var_a0_err: f64 = sigma * sigma;
474 let var_a1_err: f64 = 1f64 / 12f64;
475
476 let noise_want: f64 = noise_rgsw_product(
477 module.n() as f64,
478 basek,
479 0.5,
480 var_msg,
481 var_a0_err,
482 var_a1_err,
483 var_gct_err_lhs,
484 var_gct_err_rhs,
485 log_k_rlwe_in,
486 log_k_grlwe,
487 );
488
489 assert!(
490 (noise_have - noise_want).abs() <= 0.1,
491 "{} {}",
492 noise_have,
493 noise_want
494 );
495}
496
497#[test]
498fn external_product_inplace() {
499 let module: Module<FFT64> = Module::<FFT64>::new(2048);
500 let basek: usize = 12;
501 let log_k_grlwe: usize = 60;
502 let log_k_rlwe_in: usize = 45;
503 let log_k_rlwe_out: usize = 60;
504 let rows: usize = (log_k_rlwe_in + basek - 1) / basek;
505 let rank: usize = 1;
506
507 let sigma: f64 = 3.2;
508
509 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, log_k_grlwe, rows, rank);
510 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, log_k_rlwe_in, rank);
511 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
512 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_in);
513 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, log_k_rlwe_out);
514
515 let mut source_xs: Source = Source::new([0u8; 32]);
516 let mut source_xe: Source = Source::new([0u8; 32]);
517 let mut source_xa: Source = Source::new([0u8; 32]);
518
519 // Random input plaintext
520 pt_want
521 .data
522 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
523
524 pt_want.to_mut().at_mut(0, 0)[1] = 1;
525
526 let k: usize = 1;
527
528 pt_rgsw.raw_mut()[k] = 1; // X^{k}
529
530 let mut scratch: ScratchOwned = ScratchOwned::new(
531 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
532 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
533 | GLWECiphertext::encrypt_sk_scratch_space(&module, rank, ct_rlwe.size())
534 | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size()),
535 );
536
537 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
538 sk.fill_ternary_prob(0.5, &mut source_xs);
539
540 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
541 sk_dft.dft(&module, &sk);
542
543 ct_rgsw.encrypt_sk(
544 &module,
545 &pt_rgsw,
546 &sk_dft,
547 &mut source_xa,
548 &mut source_xe,
549 sigma,
550 scratch.borrow(),
551 );
552
553 ct_rlwe.encrypt_sk(
554 &module,
555 &pt_want,
556 &sk_dft,
557 &mut source_xa,
558 &mut source_xe,
559 sigma,
560 scratch.borrow(),
561 );
562
563 ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
564
565 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
566
567 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
568
569 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
570
571 let noise_have: f64 = pt_have.data.std(0, basek).log2();
572
573 let var_gct_err_lhs: f64 = sigma * sigma;
574 let var_gct_err_rhs: f64 = 0f64;
575
576 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
577 let var_a0_err: f64 = sigma * sigma;
578 let var_a1_err: f64 = 1f64 / 12f64;
579
580 let noise_want: f64 = noise_rgsw_product(
581 module.n() as f64,
582 basek,
583 0.5,
584 var_msg,
585 var_a0_err,
586 var_a1_err,
587 var_gct_err_lhs,
588 var_gct_err_rhs,
589 log_k_rlwe_in,
590 log_k_grlwe,
591 );
592
593 assert!(
594 (noise_have - noise_want).abs() <= 0.1,
595 "{} {}",
596 noise_have,
597 noise_want
598 );
599}