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