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}