arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/rlwe.rs
core/src/test_fft64/rlwe.rsRaw
  1#[cfg(test)]
  2mod tests_rlwe {
  3    use base2k::{
  4        Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, VecZnxToMut,
  5        ZnxViewMut, ZnxZero,
  6    };
  7    use itertools::izip;
  8    use sampling::source::Source;
  9
 10    use crate::{
 11        elem::{FromProdBy, FromProdByScratchSpace, Infos, ProdBy, ProdByScratchSpace},
 12        grlwe::GRLWECt,
 13        keys::{PublicKey, SecretKey, SecretKeyDft},
 14        rgsw::RGSWCt,
 15        rlwe::{RLWECt, RLWECtDft, RLWEPt},
 16        test_fft64::{grlwe::noise_grlwe_rlwe_product, rgsw::noise_rgsw_rlwe_product},
 17    };
 18
 19    #[test]
 20    fn encrypt_sk() {
 21        let module: Module<FFT64> = Module::<FFT64>::new(32);
 22        let log_base2k: usize = 8;
 23        let log_k_ct: usize = 54;
 24        let log_k_pt: usize = 30;
 25
 26        let sigma: f64 = 3.2;
 27        let bound: f64 = sigma * 6.0;
 28
 29        let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct);
 30        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_pt);
 31
 32        let mut source_xs: Source = Source::new([0u8; 32]);
 33        let mut source_xe: Source = Source::new([0u8; 32]);
 34        let mut source_xa: Source = Source::new([0u8; 32]);
 35
 36        let mut scratch: ScratchOwned = ScratchOwned::new(
 37            RLWECt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECt::decrypt_scratch_space(&module, ct.size()),
 38        );
 39
 40        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
 41        sk.fill_ternary_prob(0.5, &mut source_xs);
 42
 43        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
 44        sk_dft.dft(&module, &sk);
 45
 46        let mut data_want: Vec<i64> = vec![0i64; module.n()];
 47
 48        data_want
 49            .iter_mut()
 50            .for_each(|x| *x = source_xa.next_i64() & 0xFF);
 51
 52        pt.data
 53            .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
 54
 55        ct.encrypt_sk(
 56            &module,
 57            Some(&pt),
 58            &sk_dft,
 59            &mut source_xa,
 60            &mut source_xe,
 61            sigma,
 62            bound,
 63            scratch.borrow(),
 64        );
 65
 66        pt.data.zero();
 67
 68        ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
 69
 70        let mut data_have: Vec<i64> = vec![0i64; module.n()];
 71
 72        pt.data
 73            .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
 74
 75        // TODO: properly assert the decryption noise through std(dec(ct) - pt)
 76        let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
 77        izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
 78            let b_scaled = (*b as f64) / scale;
 79            assert!(
 80                (*a as f64 - b_scaled).abs() < 0.1,
 81                "{} {}",
 82                *a as f64,
 83                b_scaled
 84            )
 85        });
 86
 87        module.free();
 88    }
 89
 90    #[test]
 91    fn encrypt_zero_sk() {
 92        let module: Module<FFT64> = Module::<FFT64>::new(1024);
 93        let log_base2k: usize = 8;
 94        let log_k_ct: usize = 55;
 95
 96        let sigma: f64 = 3.2;
 97        let bound: f64 = sigma * 6.0;
 98
 99        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
100
101        let mut source_xs: Source = Source::new([0u8; 32]);
102        let mut source_xe: Source = Source::new([1u8; 32]);
103        let mut source_xa: Source = Source::new([0u8; 32]);
104
105        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
106        sk.fill_ternary_prob(0.5, &mut source_xs);
107        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
108        sk_dft.dft(&module, &sk);
109
110        let mut ct_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
111
112        let mut scratch: ScratchOwned = ScratchOwned::new(
113            RLWECtDft::decrypt_scratch_space(&module, ct_dft.size())
114                | RLWECtDft::encrypt_zero_sk_scratch_space(&module, ct_dft.size()),
115        );
116
117        ct_dft.encrypt_zero_sk(
118            &module,
119            &sk_dft,
120            &mut source_xa,
121            &mut source_xe,
122            sigma,
123            bound,
124            scratch.borrow(),
125        );
126        ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
127
128        assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
129        module.free();
130    }
131
132    #[test]
133    fn 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: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct);
143        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::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: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::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            RLWECt::encrypt_sk_scratch_space(&module, ct.size())
167                | RLWECt::decrypt_scratch_space(&module, ct.size())
168                | RLWECt::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            Some(&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: RLWEPt<Vec<u8>> = RLWEPt::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        module.free();
201    }
202
203    #[test]
204    fn from_prod_by_grlwe() {
205        let module: Module<FFT64> = Module::<FFT64>::new(2048);
206        let log_base2k: usize = 12;
207        let log_k_grlwe: usize = 60;
208        let log_k_rlwe_in: usize = 45;
209        let log_k_rlwe_out: usize = 60;
210        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
211
212        let sigma: f64 = 3.2;
213        let bound: f64 = sigma * 6.0;
214
215        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
216        let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
217        let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
218        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
219        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
220
221        let mut source_xs: Source = Source::new([0u8; 32]);
222        let mut source_xe: Source = Source::new([0u8; 32]);
223        let mut source_xa: Source = Source::new([0u8; 32]);
224
225        // Random input plaintext
226        pt_want
227            .data
228            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
229
230        let mut scratch: ScratchOwned = ScratchOwned::new(
231            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
232                | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
233                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
234                | RLWECt::from_prod_by_grlwe_scratch_space(
235                    &module,
236                    ct_rlwe_out.size(),
237                    ct_rlwe_in.size(),
238                    ct_grlwe.size(),
239                ),
240        );
241
242        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
243        sk0.fill_ternary_prob(0.5, &mut source_xs);
244
245        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
246        sk0_dft.dft(&module, &sk0);
247
248        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
249        sk1.fill_ternary_prob(0.5, &mut source_xs);
250
251        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
252        sk1_dft.dft(&module, &sk1);
253
254        ct_grlwe.encrypt_sk(
255            &module,
256            &sk0.data,
257            &sk1_dft,
258            &mut source_xa,
259            &mut source_xe,
260            sigma,
261            bound,
262            scratch.borrow(),
263        );
264
265        ct_rlwe_in.encrypt_sk(
266            &module,
267            Some(&pt_want),
268            &sk0_dft,
269            &mut source_xa,
270            &mut source_xe,
271            sigma,
272            bound,
273            scratch.borrow(),
274        );
275
276        ct_rlwe_out.from_prod_by_grlwe(&module, &ct_rlwe_in, &ct_grlwe, scratch.borrow());
277
278        ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
279
280        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
281
282        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
283        let noise_want: f64 = noise_grlwe_rlwe_product(
284            module.n() as f64,
285            log_base2k,
286            0.5,
287            0.5,
288            0f64,
289            sigma * sigma,
290            0f64,
291            log_k_rlwe_in,
292            log_k_grlwe,
293        );
294
295        assert!(
296            (noise_have - noise_want).abs() <= 0.1,
297            "{} {}",
298            noise_have,
299            noise_want
300        );
301
302        module.free();
303    }
304
305    #[test]
306    fn prod_grlwe() {
307        let module: Module<FFT64> = Module::<FFT64>::new(2048);
308        let log_base2k: usize = 12;
309        let log_k_grlwe: usize = 60;
310        let log_k_rlwe: usize = 45;
311        let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
312
313        let sigma: f64 = 3.2;
314        let bound: f64 = sigma * 6.0;
315
316        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
317        let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
318        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
319        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
320
321        let mut source_xs: Source = Source::new([0u8; 32]);
322        let mut source_xe: Source = Source::new([0u8; 32]);
323        let mut source_xa: Source = Source::new([0u8; 32]);
324
325        // Random input plaintext
326        pt_want
327            .data
328            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
329
330        let mut scratch: ScratchOwned = ScratchOwned::new(
331            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
332                | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
333                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
334                | RLWECt::prod_by_grlwe_scratch_space(&module, ct_rlwe.size(), ct_grlwe.size()),
335        );
336
337        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
338        sk0.fill_ternary_prob(0.5, &mut source_xs);
339
340        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
341        sk0_dft.dft(&module, &sk0);
342
343        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
344        sk1.fill_ternary_prob(0.5, &mut source_xs);
345
346        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
347        sk1_dft.dft(&module, &sk1);
348
349        ct_grlwe.encrypt_sk(
350            &module,
351            &sk0.data,
352            &sk1_dft,
353            &mut source_xa,
354            &mut source_xe,
355            sigma,
356            bound,
357            scratch.borrow(),
358        );
359
360        ct_rlwe.encrypt_sk(
361            &module,
362            Some(&pt_want),
363            &sk0_dft,
364            &mut source_xa,
365            &mut source_xe,
366            sigma,
367            bound,
368            scratch.borrow(),
369        );
370
371        ct_rlwe.prod_by_grlwe(&module, &ct_grlwe, scratch.borrow());
372
373        ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
374
375        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
376
377        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
378        let noise_want: f64 = noise_grlwe_rlwe_product(
379            module.n() as f64,
380            log_base2k,
381            0.5,
382            0.5,
383            0f64,
384            sigma * sigma,
385            0f64,
386            log_k_rlwe,
387            log_k_grlwe,
388        );
389
390        assert!(
391            (noise_have - noise_want).abs() <= 0.1,
392            "{} {}",
393            noise_have,
394            noise_want
395        );
396
397        module.free();
398    }
399
400    #[test]
401    fn from_prod_by_rgsw() {
402        let module: Module<FFT64> = Module::<FFT64>::new(2048);
403        let log_base2k: usize = 12;
404        let log_k_grlwe: usize = 60;
405        let log_k_rlwe_in: usize = 45;
406        let log_k_rlwe_out: usize = 60;
407        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
408
409        let sigma: f64 = 3.2;
410        let bound: f64 = sigma * 6.0;
411
412        let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
413        let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
414        let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
415        let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
416        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
417        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
418
419        let mut source_xs: Source = Source::new([0u8; 32]);
420        let mut source_xe: Source = Source::new([0u8; 32]);
421        let mut source_xa: Source = Source::new([0u8; 32]);
422
423        // Random input plaintext
424        pt_want
425            .data
426            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
427
428        pt_want.to_mut().at_mut(0, 0)[1] = 1;
429
430        let k: usize = 1;
431
432        pt_rgsw.raw_mut()[k] = 1; // X^{k}
433
434        let mut scratch: ScratchOwned = ScratchOwned::new(
435            RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
436                | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
437                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
438                | RLWECt::from_prod_by_rgsw_scratch_space(
439                    &module,
440                    ct_rlwe_out.size(),
441                    ct_rlwe_in.size(),
442                    ct_rgsw.size(),
443                ),
444        );
445
446        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
447        sk.fill_ternary_prob(0.5, &mut source_xs);
448
449        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
450        sk_dft.dft(&module, &sk);
451
452        ct_rgsw.encrypt_sk(
453            &module,
454            &pt_rgsw,
455            &sk_dft,
456            &mut source_xa,
457            &mut source_xe,
458            sigma,
459            bound,
460            scratch.borrow(),
461        );
462
463        ct_rlwe_in.encrypt_sk(
464            &module,
465            Some(&pt_want),
466            &sk_dft,
467            &mut source_xa,
468            &mut source_xe,
469            sigma,
470            bound,
471            scratch.borrow(),
472        );
473
474        ct_rlwe_out.from_prod_by_rgsw(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
475
476        ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
477
478        module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
479
480        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
481
482        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
483
484        let var_gct_err_lhs: f64 = sigma * sigma;
485        let var_gct_err_rhs: f64 = 0f64;
486
487        let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
488        let var_a0_err: f64 = sigma * sigma;
489        let var_a1_err: f64 = 1f64 / 12f64;
490
491        let noise_want: f64 = noise_rgsw_rlwe_product(
492            module.n() as f64,
493            log_base2k,
494            0.5,
495            var_msg,
496            var_a0_err,
497            var_a1_err,
498            var_gct_err_lhs,
499            var_gct_err_rhs,
500            log_k_rlwe_in,
501            log_k_grlwe,
502        );
503
504        assert!(
505            (noise_have - noise_want).abs() <= 0.1,
506            "{} {}",
507            noise_have,
508            noise_want
509        );
510
511        module.free();
512    }
513
514    #[test]
515    fn prod_by_rgsw() {
516        let module: Module<FFT64> = Module::<FFT64>::new(2048);
517        let log_base2k: usize = 12;
518        let log_k_grlwe: usize = 60;
519        let log_k_rlwe_in: usize = 45;
520        let log_k_rlwe_out: usize = 60;
521        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
522
523        let sigma: f64 = 3.2;
524        let bound: f64 = sigma * 6.0;
525
526        let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
527        let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
528        let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
529        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
530        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
531
532        let mut source_xs: Source = Source::new([0u8; 32]);
533        let mut source_xe: Source = Source::new([0u8; 32]);
534        let mut source_xa: Source = Source::new([0u8; 32]);
535
536        // Random input plaintext
537        pt_want
538            .data
539            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
540
541        pt_want.to_mut().at_mut(0, 0)[1] = 1;
542
543        let k: usize = 1;
544
545        pt_rgsw.raw_mut()[k] = 1; // X^{k}
546
547        let mut scratch: ScratchOwned = ScratchOwned::new(
548            RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
549                | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
550                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
551                | RLWECt::prod_by_rgsw_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size()),
552        );
553
554        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
555        sk.fill_ternary_prob(0.5, &mut source_xs);
556
557        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
558        sk_dft.dft(&module, &sk);
559
560        ct_rgsw.encrypt_sk(
561            &module,
562            &pt_rgsw,
563            &sk_dft,
564            &mut source_xa,
565            &mut source_xe,
566            sigma,
567            bound,
568            scratch.borrow(),
569        );
570
571        ct_rlwe.encrypt_sk(
572            &module,
573            Some(&pt_want),
574            &sk_dft,
575            &mut source_xa,
576            &mut source_xe,
577            sigma,
578            bound,
579            scratch.borrow(),
580        );
581
582        ct_rlwe.prod_by_rgsw(&module, &ct_rgsw, scratch.borrow());
583
584        ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
585
586        module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
587
588        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
589
590        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
591
592        let var_gct_err_lhs: f64 = sigma * sigma;
593        let var_gct_err_rhs: f64 = 0f64;
594
595        let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
596        let var_a0_err: f64 = sigma * sigma;
597        let var_a1_err: f64 = 1f64 / 12f64;
598
599        let noise_want: f64 = noise_rgsw_rlwe_product(
600            module.n() as f64,
601            log_base2k,
602            0.5,
603            var_msg,
604            var_a0_err,
605            var_a1_err,
606            var_gct_err_lhs,
607            var_gct_err_rhs,
608            log_k_rlwe_in,
609            log_k_grlwe,
610        );
611
612        assert!(
613            (noise_have - noise_want).abs() <= 0.1,
614            "{} {}",
615            noise_have,
616            noise_want
617        );
618
619        module.free();
620    }
621}