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