arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/gglwe/tests/generics_gglwe.rs
core/src/gglwe/tests/generics_gglwe.rsRaw
  1use backend::hal::{
  2    api::{
  3        ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxCopy, VecZnxRotateInplace, VecZnxStd,
  4        VecZnxSubScalarInplace, VecZnxSwithcDegree, ZnxViewMut,
  5    },
  6    layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
  7    oep::{
  8        ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
  9        TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
 10    },
 11};
 12use sampling::source::Source;
 13
 14use crate::{
 15    GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
 16    GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed,
 17    GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
 18    noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
 19};
 20
 21pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
 22    + GLWEDecryptFamily<B>
 23    + VecZnxSwithcDegree
 24    + VecZnxAddScalarInplace
 25    + VecZnxStd
 26    + VecZnxSubScalarInplace
 27    + VecZnxCopy;
 28
 29pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
 30    + TakeVecZnxBigImpl<B>
 31    + TakeSvpPPolImpl<B>
 32    + ScratchOwnedAllocImpl<B>
 33    + ScratchOwnedBorrowImpl<B>
 34    + ScratchAvailableImpl<B>
 35    + TakeScalarZnxImpl<B>
 36    + TakeVecZnxImpl<B>
 37    + VecZnxDftAllocBytesImpl<B>
 38    + VecZnxBigAllocBytesImpl<B>
 39    + TakeSvpPPolImpl<B>;
 40
 41pub(crate) fn test_gglwe_encrypt_sk<B: Backend>(
 42    module: &Module<B>,
 43    basek: usize,
 44    k_ksk: usize,
 45    digits: usize,
 46    rank_in: usize,
 47    rank_out: usize,
 48    sigma: f64,
 49) where
 50    Module<B>: TestModuleFamily<B>,
 51    B: TestScratchFamily<B>,
 52{
 53    let n: usize = module.n();
 54    let rows: usize = (k_ksk - digits * basek) / (digits * basek);
 55
 56    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(n, basek, k_ksk, rows, digits, rank_in, rank_out);
 57
 58    let mut source_xs: Source = Source::new([0u8; 32]);
 59    let mut source_xe: Source = Source::new([0u8; 32]);
 60    let mut source_xa: Source = Source::new([0u8; 32]);
 61
 62    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
 63        module, n, basek, k_ksk, rank_in, rank_out,
 64    ));
 65
 66    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in);
 67    sk_in.fill_ternary_prob(0.5, &mut source_xs);
 68
 69    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
 70    sk_out.fill_ternary_prob(0.5, &mut source_xs);
 71    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
 72
 73    ksk.encrypt_sk(
 74        module,
 75        &sk_in,
 76        &sk_out,
 77        &mut source_xa,
 78        &mut source_xe,
 79        sigma,
 80        scratch.borrow(),
 81    );
 82
 83    ksk.key
 84        .assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
 85}
 86
 87pub(crate) fn test_gglwe_encrypt_sk_compressed<B: Backend>(
 88    module: &Module<B>,
 89    basek: usize,
 90    k_ksk: usize,
 91    digits: usize,
 92    rank_in: usize,
 93    rank_out: usize,
 94    sigma: f64,
 95) where
 96    Module<B>: TestModuleFamily<B>,
 97    B: TestScratchFamily<B>,
 98{
 99    let n: usize = module.n();
100    let rows: usize = (k_ksk - digits * basek) / (digits * basek);
101
102    let mut ksk_compressed: GLWESwitchingKeyCompressed<Vec<u8>> =
103        GLWESwitchingKeyCompressed::alloc(n, basek, k_ksk, rows, digits, rank_in, rank_out);
104
105    let mut source_xs: Source = Source::new([0u8; 32]);
106    let mut source_xe: Source = Source::new([0u8; 32]);
107
108    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(
109        module, n, basek, k_ksk, rank_in, rank_out,
110    ));
111
112    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in);
113    sk_in.fill_ternary_prob(0.5, &mut source_xs);
114
115    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
116    sk_out.fill_ternary_prob(0.5, &mut source_xs);
117    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
118
119    let seed_xa = [1u8; 32];
120
121    ksk_compressed.encrypt_sk(
122        module,
123        &sk_in,
124        &sk_out,
125        seed_xa,
126        &mut source_xe,
127        sigma,
128        scratch.borrow(),
129    );
130
131    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(n, basek, k_ksk, rows, digits, rank_in, rank_out);
132    ksk.decompress(module, &ksk_compressed);
133
134    ksk.key
135        .assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
136}
137
138pub(crate) fn test_gglwe_keyswitch<B: Backend>(
139    module: &Module<B>,
140    basek: usize,
141    k_out: usize,
142    k_in: usize,
143    k_ksk: usize,
144    digits: usize,
145    rank_in_s0s1: usize,
146    rank_out_s0s1: usize,
147    rank_out_s1s2: usize,
148    sigma: f64,
149) where
150    Module<B>:
151        TestModuleFamily<B> + GGLWEEncryptSkFamily<B> + GLWEDecryptFamily<B> + GLWEKeyswitchFamily<B> + GGLWEExecLayoutFamily<B>,
152    B: TestScratchFamily<B>,
153{
154    let n: usize = module.n();
155    let rows: usize = k_in.div_ceil(basek * digits);
156    let digits_in: usize = 1;
157
158    let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> =
159        GLWESwitchingKey::alloc(n, basek, k_in, rows, digits_in, rank_in_s0s1, rank_out_s0s1);
160    let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> =
161        GLWESwitchingKey::alloc(n, basek, k_ksk, rows, digits, rank_out_s0s1, rank_out_s1s2);
162    let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
163        n,
164        basek,
165        k_out,
166        rows,
167        digits_in,
168        rank_in_s0s1,
169        rank_out_s1s2,
170    );
171
172    let mut source_xs: Source = Source::new([0u8; 32]);
173    let mut source_xe: Source = Source::new([0u8; 32]);
174    let mut source_xa: Source = Source::new([0u8; 32]);
175
176    let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
177        module,
178        n,
179        basek,
180        k_ksk,
181        rank_in_s0s1 | rank_out_s0s1,
182        rank_out_s0s1 | rank_out_s1s2,
183    ));
184    let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_scratch_space(
185        module,
186        n,
187        basek,
188        k_out,
189        k_in,
190        k_ksk,
191        digits,
192        ct_gglwe_s1s2.rank_in(),
193        ct_gglwe_s1s2.rank_out(),
194    ));
195
196    let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in_s0s1);
197    sk0.fill_ternary_prob(0.5, &mut source_xs);
198
199    let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out_s0s1);
200    sk1.fill_ternary_prob(0.5, &mut source_xs);
201
202    let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out_s1s2);
203    sk2.fill_ternary_prob(0.5, &mut source_xs);
204    let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
205
206    // gglwe_{s1}(s0) = s0 -> s1
207    ct_gglwe_s0s1.encrypt_sk(
208        module,
209        &sk0,
210        &sk1,
211        &mut source_xa,
212        &mut source_xe,
213        sigma,
214        scratch_enc.borrow(),
215    );
216
217    // gglwe_{s2}(s1) -> s1 -> s2
218    ct_gglwe_s1s2.encrypt_sk(
219        module,
220        &sk1,
221        &sk2,
222        &mut source_xa,
223        &mut source_xe,
224        sigma,
225        scratch_enc.borrow(),
226    );
227
228    let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
229        GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
230
231    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
232    ct_gglwe_s0s2.keyswitch(
233        module,
234        &ct_gglwe_s0s1,
235        &ct_gglwe_s1s2_exec,
236        scratch_apply.borrow(),
237    );
238
239    let max_noise: f64 = log2_std_noise_gglwe_product(
240        n as f64,
241        basek * digits,
242        0.5,
243        0.5,
244        0f64,
245        sigma * sigma,
246        0f64,
247        rank_out_s0s1 as f64,
248        k_in,
249        k_ksk,
250    );
251
252    ct_gglwe_s0s2
253        .key
254        .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
255}
256
257pub(crate) fn test_gglwe_keyswitch_inplace<B: Backend>(
258    module: &Module<B>,
259    basek: usize,
260    k_ct: usize,
261    k_ksk: usize,
262    digits: usize,
263    rank_in: usize,
264    rank_out: usize,
265    sigma: f64,
266) where
267    Module<B>: TestModuleFamily<B>
268        + GLWESwitchingKeyEncryptSkFamily<B>
269        + GLWEKeyswitchFamily<B>
270        + GGLWEExecLayoutFamily<B>
271        + GLWEDecryptFamily<B>,
272    B: TestScratchFamily<B>,
273{
274    let n: usize = module.n();
275    let rows: usize = k_ct.div_ceil(basek * digits);
276    let digits_in: usize = 1;
277
278    let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> =
279        GLWESwitchingKey::alloc(n, basek, k_ct, rows, digits_in, rank_in, rank_out);
280    let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(n, basek, k_ksk, rows, digits, rank_out, rank_out);
281
282    let mut source_xs: Source = Source::new([0u8; 32]);
283    let mut source_xe: Source = Source::new([0u8; 32]);
284    let mut source_xa: Source = Source::new([0u8; 32]);
285
286    let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
287        module,
288        n,
289        basek,
290        k_ksk,
291        rank_in | rank_out,
292        rank_out,
293    ));
294    let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_inplace_scratch_space(
295        module, n, basek, k_ct, k_ksk, digits, rank_out,
296    ));
297
298    let var_xs: f64 = 0.5;
299
300    let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in);
301    sk0.fill_ternary_prob(var_xs, &mut source_xs);
302
303    let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
304    sk1.fill_ternary_prob(var_xs, &mut source_xs);
305
306    let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
307    sk2.fill_ternary_prob(var_xs, &mut source_xs);
308    let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
309
310    // gglwe_{s1}(s0) = s0 -> s1
311    ct_gglwe_s0s1.encrypt_sk(
312        module,
313        &sk0,
314        &sk1,
315        &mut source_xa,
316        &mut source_xe,
317        sigma,
318        scratch_enc.borrow(),
319    );
320
321    // gglwe_{s2}(s1) -> s1 -> s2
322    ct_gglwe_s1s2.encrypt_sk(
323        module,
324        &sk1,
325        &sk2,
326        &mut source_xa,
327        &mut source_xe,
328        sigma,
329        scratch_enc.borrow(),
330    );
331
332    let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
333        GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
334
335    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
336    ct_gglwe_s0s1.keyswitch_inplace(module, &ct_gglwe_s1s2_exec, scratch_apply.borrow());
337
338    let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = ct_gglwe_s0s1;
339
340    let max_noise: f64 = log2_std_noise_gglwe_product(
341        n as f64,
342        basek * digits,
343        var_xs,
344        var_xs,
345        0f64,
346        sigma * sigma,
347        0f64,
348        rank_out as f64,
349        k_ct,
350        k_ksk,
351    );
352
353    ct_gglwe_s0s2
354        .key
355        .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
356}
357
358pub(crate) fn test_gglwe_external_product<B: Backend>(
359    module: &Module<B>,
360    basek: usize,
361    k_out: usize,
362    k_in: usize,
363    k_ggsw: usize,
364    digits: usize,
365    rank_in: usize,
366    rank_out: usize,
367    sigma: f64,
368) where
369    Module<B>: TestModuleFamily<B>
370        + GLWESwitchingKeyEncryptSkFamily<B>
371        + GLWEExternalProductFamily<B>
372        + GGSWLayoutFamily<B>
373        + GLWEDecryptFamily<B>
374        + VecZnxRotateInplace,
375    B: TestScratchFamily<B>,
376{
377    let n: usize = module.n();
378    let rows: usize = k_in.div_ceil(basek * digits);
379    let digits_in: usize = 1;
380
381    let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(n, basek, k_in, rows, digits_in, rank_in, rank_out);
382    let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>> =
383        GLWESwitchingKey::alloc(n, basek, k_out, rows, digits_in, rank_in, rank_out);
384    let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw, rows, digits, rank_out);
385
386    let mut pt_rgsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
387
388    let mut source_xs: Source = Source::new([0u8; 32]);
389    let mut source_xe: Source = Source::new([0u8; 32]);
390    let mut source_xa: Source = Source::new([0u8; 32]);
391
392    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
393        GLWESwitchingKey::encrypt_sk_scratch_space(module, n, basek, k_in, rank_in, rank_out)
394            | GLWESwitchingKey::external_product_scratch_space(module, n, basek, k_out, k_in, k_ggsw, digits, rank_out)
395            | GGSWCiphertext::encrypt_sk_scratch_space(module, n, basek, k_ggsw, rank_out),
396    );
397
398    let r: usize = 1;
399
400    pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
401
402    let var_xs: f64 = 0.5;
403
404    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in);
405    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
406
407    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
408    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
409    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
410
411    // gglwe_{s1}(s0) = s0 -> s1
412    ct_gglwe_in.encrypt_sk(
413        module,
414        &sk_in,
415        &sk_out,
416        &mut source_xa,
417        &mut source_xe,
418        sigma,
419        scratch.borrow(),
420    );
421
422    ct_rgsw.encrypt_sk(
423        module,
424        &pt_rgsw,
425        &sk_out_exec,
426        &mut source_xa,
427        &mut source_xe,
428        sigma,
429        scratch.borrow(),
430    );
431
432    let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
433        GGSWCiphertextExec::alloc(module, n, basek, k_ggsw, rows, digits, rank_out);
434
435    ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
436
437    // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
438    ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow());
439
440    (0..rank_in).for_each(|i| {
441        module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
442    });
443
444    let var_gct_err_lhs: f64 = sigma * sigma;
445    let var_gct_err_rhs: f64 = 0f64;
446
447    let var_msg: f64 = 1f64 / n as f64; // X^{k}
448    let var_a0_err: f64 = sigma * sigma;
449    let var_a1_err: f64 = 1f64 / 12f64;
450
451    let max_noise: f64 = noise_ggsw_product(
452        n as f64,
453        basek * digits,
454        var_xs,
455        var_msg,
456        var_a0_err,
457        var_a1_err,
458        var_gct_err_lhs,
459        var_gct_err_rhs,
460        rank_out as f64,
461        k_in,
462        k_ggsw,
463    );
464
465    ct_gglwe_out
466        .key
467        .assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
468}
469
470pub(crate) fn test_gglwe_external_product_inplace<B: Backend>(
471    module: &Module<B>,
472    basek: usize,
473    k_ct: usize,
474    k_ggsw: usize,
475    digits: usize,
476    rank_in: usize,
477    rank_out: usize,
478    sigma: f64,
479) where
480    Module<B>: TestModuleFamily<B>
481        + GLWESwitchingKeyEncryptSkFamily<B>
482        + GLWEExternalProductFamily<B>
483        + GGSWLayoutFamily<B>
484        + GLWEDecryptFamily<B>
485        + VecZnxRotateInplace,
486    B: TestScratchFamily<B>,
487{
488    let n: usize = module.n();
489    let rows: usize = k_ct.div_ceil(basek * digits);
490
491    let digits_in: usize = 1;
492
493    let mut ct_gglwe: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(n, basek, k_ct, rows, digits_in, rank_in, rank_out);
494    let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw, rows, digits, rank_out);
495
496    let mut pt_rgsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
497
498    let mut source_xs: Source = Source::new([0u8; 32]);
499    let mut source_xe: Source = Source::new([0u8; 32]);
500    let mut source_xa: Source = Source::new([0u8; 32]);
501
502    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
503        GLWESwitchingKey::encrypt_sk_scratch_space(module, n, basek, k_ct, rank_in, rank_out)
504            | GLWESwitchingKey::external_product_inplace_scratch_space(module, n, basek, k_ct, k_ggsw, digits, rank_out)
505            | GGSWCiphertext::encrypt_sk_scratch_space(module, n, basek, k_ggsw, rank_out),
506    );
507
508    let r: usize = 1;
509
510    pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
511
512    let var_xs: f64 = 0.5;
513
514    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_in);
515    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
516
517    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank_out);
518    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
519    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
520
521    // gglwe_{s1}(s0) = s0 -> s1
522    ct_gglwe.encrypt_sk(
523        module,
524        &sk_in,
525        &sk_out,
526        &mut source_xa,
527        &mut source_xe,
528        sigma,
529        scratch.borrow(),
530    );
531
532    ct_rgsw.encrypt_sk(
533        module,
534        &pt_rgsw,
535        &sk_out_exec,
536        &mut source_xa,
537        &mut source_xe,
538        sigma,
539        scratch.borrow(),
540    );
541
542    let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
543        GGSWCiphertextExec::alloc(module, n, basek, k_ggsw, rows, digits, rank_out);
544
545    ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
546
547    // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
548    ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow());
549
550    (0..rank_in).for_each(|i| {
551        module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
552    });
553
554    let var_gct_err_lhs: f64 = sigma * sigma;
555    let var_gct_err_rhs: f64 = 0f64;
556
557    let var_msg: f64 = 1f64 / n as f64; // X^{k}
558    let var_a0_err: f64 = sigma * sigma;
559    let var_a1_err: f64 = 1f64 / 12f64;
560
561    let max_noise: f64 = noise_ggsw_product(
562        n as f64,
563        basek * digits,
564        var_xs,
565        var_msg,
566        var_a0_err,
567        var_a1_err,
568        var_gct_err_lhs,
569        var_gct_err_rhs,
570        rank_out as f64,
571        k_ct,
572        k_ggsw,
573    );
574
575    ct_gglwe
576        .key
577        .assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
578}