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