arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/ggsw/test/generic_tests.rs
core/src/ggsw/test/generic_tests.rsRaw
  1use backend::hal::{
  2    api::{
  3        MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
  4        VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxRotateInplace, VecZnxStd,
  5        VecZnxSubABInplace, VecZnxSwithcDegree, 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    AutomorphismKey, AutomorphismKeyExec, GGLWEExecLayoutFamily, GGSWAssertNoiseFamily, GGSWCiphertext, GGSWCiphertextExec,
 17    GGSWEncryptSkFamily, GGSWKeySwitchFamily, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKey,
 18    GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec, GLWETensorKey, GLWETensorKeyEncryptSkFamily, GLWETensorKeyExec,
 19    noise::{noise_ggsw_keyswitch, noise_ggsw_product},
 20};
 21
 22pub(crate) trait TestModuleFamily<B: Backend> = GLWESecretFamily<B>
 23    + GGSWEncryptSkFamily<B>
 24    + GGSWAssertNoiseFamily<B>
 25    + VecZnxAlloc
 26    + ScalarZnxAlloc
 27    + VecZnxAllocBytes
 28    + MatZnxAlloc
 29    + VecZnxAddScalarInplace
 30    + VecZnxSubABInplace
 31    + VecZnxStd
 32    + ScalarZnxAllocBytes;
 33pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
 34    + TakeVecZnxBigImpl<B>
 35    + TakeSvpPPolImpl<B>
 36    + ScratchOwnedAllocImpl<B>
 37    + ScratchOwnedBorrowImpl<B>
 38    + ScratchAvailableImpl<B>
 39    + TakeScalarZnxImpl<B>
 40    + TakeVecZnxImpl<B>
 41    + VecZnxDftAllocBytesImpl<B>
 42    + VecZnxBigAllocBytesImpl<B>
 43    + TakeSvpPPolImpl<B>;
 44
 45pub(crate) fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, digits: usize, rank: usize, sigma: f64)
 46where
 47    Module<B>: TestModuleFamily<B>,
 48    B: TestScratchFamily<B>,
 49{
 50    let rows: usize = (k - digits * basek) / (digits * basek);
 51
 52    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k, rows, digits, rank);
 53
 54    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
 55
 56    let mut source_xs: Source = Source::new([0u8; 32]);
 57    let mut source_xe: Source = Source::new([0u8; 32]);
 58    let mut source_xa: Source = Source::new([0u8; 32]);
 59
 60    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 61
 62    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GGSWCiphertext::encrypt_sk_scratch_space(
 63        module, basek, k, rank,
 64    ));
 65
 66    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
 67    sk.fill_ternary_prob(0.5, &mut source_xs);
 68    let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
 69    sk_exec.prepare(module, &sk);
 70
 71    ct.encrypt_sk(
 72        module,
 73        &pt_scalar,
 74        &sk_exec,
 75        &mut source_xa,
 76        &mut source_xe,
 77        sigma,
 78        scratch.borrow(),
 79    );
 80
 81    let noise_f = |_col_i: usize| -(k as f64) + sigma.log2() + 0.5;
 82
 83    ct.assert_noise(module, &sk_exec, &pt_scalar, &noise_f);
 84}
 85
 86pub(crate) fn test_keyswitch<B: Backend>(
 87    module: &Module<B>,
 88    basek: usize,
 89    k_out: usize,
 90    k_in: usize,
 91    k_ksk: usize,
 92    k_tsk: usize,
 93    digits: usize,
 94    rank: usize,
 95    sigma: f64,
 96) where
 97    Module<B>: TestModuleFamily<B>
 98        + GGSWAssertNoiseFamily<B>
 99        + GGSWKeySwitchFamily<B>
100        + GLWESwitchingKeyEncryptSkFamily<B>
101        + GLWETensorKeyEncryptSkFamily<B>
102        + GGLWEExecLayoutFamily<B>
103        + VecZnxSwithcDegree,
104    B: TestScratchFamily<B> + VecZnxDftAllocBytesImpl<B> + VecZnxBigAllocBytesImpl<B> + TakeSvpPPolImpl<B>,
105{
106    let rows: usize = k_in.div_ceil(digits * basek);
107
108    let digits_in: usize = 1;
109
110    let mut ct_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows, digits_in, rank);
111    let mut ct_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows, digits_in, rank);
112    let mut tsk: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_ksk, rows, digits, rank);
113    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank, rank);
114    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
115
116    let mut source_xs: Source = Source::new([0u8; 32]);
117    let mut source_xe: Source = Source::new([0u8; 32]);
118    let mut source_xa: Source = Source::new([0u8; 32]);
119
120    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
121        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_in, rank)
122            | GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank, rank)
123            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
124            | GGSWCiphertext::keyswitch_scratch_space(
125                module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
126            ),
127    );
128
129    let var_xs: f64 = 0.5;
130
131    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
132    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
133    let sk_in_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_in);
134
135    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
136    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
137    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
138
139    ksk.encrypt_sk(
140        module,
141        &sk_in,
142        &sk_out,
143        &mut source_xa,
144        &mut source_xe,
145        sigma,
146        scratch.borrow(),
147    );
148    tsk.encrypt_sk(
149        module,
150        &sk_out,
151        &mut source_xa,
152        &mut source_xe,
153        sigma,
154        scratch.borrow(),
155    );
156
157    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
158
159    ct_in.encrypt_sk(
160        module,
161        &pt_scalar,
162        &sk_in_dft,
163        &mut source_xa,
164        &mut source_xe,
165        sigma,
166        scratch.borrow(),
167    );
168
169    let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
170        GLWESwitchingKeyExec::alloc(module, basek, k_ksk, rows, digits, rank, rank);
171    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
172
173    ksk_exec.prepare(module, &ksk, scratch.borrow());
174    tsk_exec.prepare(module, &tsk, scratch.borrow());
175
176    ct_out.keyswitch(module, &ct_in, &ksk_exec, &tsk_exec, scratch.borrow());
177
178    let max_noise = |col_j: usize| -> f64 {
179        noise_ggsw_keyswitch(
180            module.n() as f64,
181            basek * digits,
182            col_j,
183            var_xs,
184            0f64,
185            sigma * sigma,
186            0f64,
187            rank as f64,
188            k_in,
189            k_ksk,
190            k_tsk,
191        ) + 0.5
192    };
193
194    ct_out.assert_noise(module, &sk_out_exec, &pt_scalar, &max_noise);
195}
196
197pub(crate) fn test_keyswitch_inplace<B: Backend>(
198    module: &Module<B>,
199    basek: usize,
200    k_ct: usize,
201    k_ksk: usize,
202    k_tsk: usize,
203    digits: usize,
204    rank: usize,
205    sigma: f64,
206) where
207    Module<B>: TestModuleFamily<B>
208        + GGSWAssertNoiseFamily<B>
209        + GGSWKeySwitchFamily<B>
210        + GLWESwitchingKeyEncryptSkFamily<B>
211        + GLWETensorKeyEncryptSkFamily<B>
212        + GGLWEExecLayoutFamily<B>
213        + VecZnxSwithcDegree,
214    B: TestScratchFamily<B>,
215{
216    let rows: usize = k_ct.div_ceil(digits * basek);
217
218    let digits_in: usize = 1;
219
220    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows, digits_in, rank);
221    let mut tsk: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
222    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank, rank);
223    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
224
225    let mut source_xs: Source = Source::new([0u8; 32]);
226    let mut source_xe: Source = Source::new([0u8; 32]);
227    let mut source_xa: Source = Source::new([0u8; 32]);
228
229    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
230        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ct, rank)
231            | GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank, rank)
232            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
233            | GGSWCiphertext::keyswitch_inplace_scratch_space(module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
234    );
235
236    let var_xs: f64 = 0.5;
237
238    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
239    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
240    let sk_in_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_in);
241
242    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
243    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
244    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
245
246    ksk.encrypt_sk(
247        module,
248        &sk_in,
249        &sk_out,
250        &mut source_xa,
251        &mut source_xe,
252        sigma,
253        scratch.borrow(),
254    );
255    tsk.encrypt_sk(
256        module,
257        &sk_out,
258        &mut source_xa,
259        &mut source_xe,
260        sigma,
261        scratch.borrow(),
262    );
263
264    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
265
266    ct.encrypt_sk(
267        module,
268        &pt_scalar,
269        &sk_in_dft,
270        &mut source_xa,
271        &mut source_xe,
272        sigma,
273        scratch.borrow(),
274    );
275
276    let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
277        GLWESwitchingKeyExec::alloc(module, basek, k_ksk, rows, digits, rank, rank);
278    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
279
280    ksk_exec.prepare(module, &ksk, scratch.borrow());
281    tsk_exec.prepare(module, &tsk, scratch.borrow());
282
283    ct.keyswitch_inplace(module, &ksk_exec, &tsk_exec, scratch.borrow());
284
285    let max_noise = |col_j: usize| -> f64 {
286        noise_ggsw_keyswitch(
287            module.n() as f64,
288            basek * digits,
289            col_j,
290            var_xs,
291            0f64,
292            sigma * sigma,
293            0f64,
294            rank as f64,
295            k_ct,
296            k_ksk,
297            k_tsk,
298        ) + 0.5
299    };
300
301    ct.assert_noise(module, &sk_out_exec, &pt_scalar, &max_noise);
302}
303
304pub(crate) fn test_automorphism<B: Backend>(
305    p: i64,
306    module: &Module<B>,
307    basek: usize,
308    k_out: usize,
309    k_in: usize,
310    k_ksk: usize,
311    k_tsk: usize,
312    digits: usize,
313    rank: usize,
314    sigma: f64,
315) where
316    Module<B>: TestModuleFamily<B>
317        + GGSWAssertNoiseFamily<B>
318        + GGSWKeySwitchFamily<B>
319        + GLWESwitchingKeyEncryptSkFamily<B>
320        + GLWETensorKeyEncryptSkFamily<B>
321        + GGLWEExecLayoutFamily<B>
322        + VecZnxSwithcDegree
323        + VecZnxAutomorphismInplace
324        + VecZnxAutomorphismInplace
325        + VecZnxAutomorphism,
326    B: TestScratchFamily<B>,
327{
328    let rows: usize = k_in.div_ceil(basek * digits);
329    let rows_in: usize = k_in.div_euclid(basek * digits);
330
331    let digits_in: usize = 1;
332
333    let mut ct_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows_in, digits_in, rank);
334    let mut ct_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows_in, digits_in, rank);
335    let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
336    let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
337    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
338
339    let mut source_xs: Source = Source::new([0u8; 32]);
340    let mut source_xe: Source = Source::new([0u8; 32]);
341    let mut source_xa: Source = Source::new([0u8; 32]);
342
343    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
344        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_in, rank)
345            | AutomorphismKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank)
346            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
347            | GGSWCiphertext::automorphism_scratch_space(
348                module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
349            ),
350    );
351
352    let var_xs: f64 = 0.5;
353
354    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
355    sk.fill_ternary_prob(var_xs, &mut source_xs);
356    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
357
358    auto_key.encrypt_sk(
359        module,
360        p,
361        &sk,
362        &mut source_xa,
363        &mut source_xe,
364        sigma,
365        scratch.borrow(),
366    );
367    tensor_key.encrypt_sk(
368        module,
369        &sk,
370        &mut source_xa,
371        &mut source_xe,
372        sigma,
373        scratch.borrow(),
374    );
375
376    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
377
378    ct_in.encrypt_sk(
379        module,
380        &pt_scalar,
381        &sk_exec,
382        &mut source_xa,
383        &mut source_xe,
384        sigma,
385        scratch.borrow(),
386    );
387
388    let mut auto_key_exec: AutomorphismKeyExec<Vec<u8>, B> = AutomorphismKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
389    auto_key_exec.prepare(module, &auto_key, scratch.borrow());
390
391    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_tsk, rows, digits, rank);
392    tsk_exec.prepare(module, &tensor_key, scratch.borrow());
393
394    ct_out.automorphism(module, &ct_in, &auto_key_exec, &tsk_exec, scratch.borrow());
395
396    module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
397
398    let max_noise = |col_j: usize| -> f64 {
399        noise_ggsw_keyswitch(
400            module.n() as f64,
401            basek * digits,
402            col_j,
403            var_xs,
404            0f64,
405            sigma * sigma,
406            0f64,
407            rank as f64,
408            k_in,
409            k_ksk,
410            k_tsk,
411        ) + 0.5
412    };
413
414    ct_out.assert_noise(module, &sk_exec, &pt_scalar, &max_noise);
415}
416
417pub(crate) fn test_automorphism_inplace<B: Backend>(
418    p: i64,
419    module: &Module<B>,
420    basek: usize,
421    k_ct: usize,
422    k_ksk: usize,
423    k_tsk: usize,
424    digits: usize,
425    rank: usize,
426    sigma: f64,
427) where
428    Module<B>: TestModuleFamily<B>
429        + GGSWAssertNoiseFamily<B>
430        + GGSWKeySwitchFamily<B>
431        + GLWESwitchingKeyEncryptSkFamily<B>
432        + GLWETensorKeyEncryptSkFamily<B>
433        + GGLWEExecLayoutFamily<B>
434        + VecZnxSwithcDegree
435        + VecZnxAutomorphismInplace
436        + VecZnxAutomorphism
437        + VecZnxAutomorphismInplace,
438    B: TestScratchFamily<B>,
439{
440    let rows: usize = k_ct.div_ceil(digits * basek);
441    let rows_in: usize = k_ct.div_euclid(basek * digits);
442    let digits_in: usize = 1;
443
444    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows_in, digits_in, rank);
445    let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
446    let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
447    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
448
449    let mut source_xs: Source = Source::new([0u8; 32]);
450    let mut source_xe: Source = Source::new([0u8; 32]);
451    let mut source_xa: Source = Source::new([0u8; 32]);
452
453    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
454        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ct, rank)
455            | AutomorphismKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank)
456            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
457            | GGSWCiphertext::automorphism_inplace_scratch_space(module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
458    );
459
460    let var_xs: f64 = 0.5;
461
462    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
463    sk.fill_ternary_prob(var_xs, &mut source_xs);
464    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
465
466    auto_key.encrypt_sk(
467        module,
468        p,
469        &sk,
470        &mut source_xa,
471        &mut source_xe,
472        sigma,
473        scratch.borrow(),
474    );
475    tensor_key.encrypt_sk(
476        module,
477        &sk,
478        &mut source_xa,
479        &mut source_xe,
480        sigma,
481        scratch.borrow(),
482    );
483
484    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
485
486    ct.encrypt_sk(
487        module,
488        &pt_scalar,
489        &sk_exec,
490        &mut source_xa,
491        &mut source_xe,
492        sigma,
493        scratch.borrow(),
494    );
495
496    let mut auto_key_exec: AutomorphismKeyExec<Vec<u8>, B> = AutomorphismKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
497    auto_key_exec.prepare(module, &auto_key, scratch.borrow());
498
499    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_tsk, rows, digits, rank);
500    tsk_exec.prepare(module, &tensor_key, scratch.borrow());
501
502    ct.automorphism_inplace(module, &auto_key_exec, &tsk_exec, scratch.borrow());
503
504    module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
505
506    let max_noise = |col_j: usize| -> f64 {
507        noise_ggsw_keyswitch(
508            module.n() as f64,
509            basek * digits,
510            col_j,
511            var_xs,
512            0f64,
513            sigma * sigma,
514            0f64,
515            rank as f64,
516            k_ct,
517            k_ksk,
518            k_tsk,
519        ) + 0.5
520    };
521
522    ct.assert_noise(module, &sk_exec, &pt_scalar, &max_noise);
523}
524
525pub(crate) fn test_external_product<B: Backend>(
526    module: &Module<B>,
527    basek: usize,
528    k_in: usize,
529    k_out: usize,
530    k_ggsw: usize,
531    digits: usize,
532    rank: usize,
533    sigma: f64,
534) where
535    Module<B>: TestModuleFamily<B>
536        + GGSWAssertNoiseFamily<B>
537        + GGSWKeySwitchFamily<B>
538        + GLWESwitchingKeyEncryptSkFamily<B>
539        + GLWETensorKeyEncryptSkFamily<B>
540        + GGLWEExecLayoutFamily<B>
541        + VecZnxRotateInplace,
542    B: TestScratchFamily<B>,
543{
544    let rows: usize = k_in.div_ceil(basek * digits);
545    let rows_in: usize = k_in.div_euclid(basek * digits);
546    let digits_in: usize = 1;
547
548    let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows_in, digits_in, rank);
549    let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows_in, digits_in, rank);
550    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank);
551    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
552    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
553
554    let mut source_xs: Source = Source::new([0u8; 32]);
555    let mut source_xe: Source = Source::new([0u8; 32]);
556    let mut source_xa: Source = Source::new([0u8; 32]);
557
558    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
559
560    let k: usize = 1;
561
562    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
563
564    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
565        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank)
566            | GGSWCiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank),
567    );
568
569    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
570    sk.fill_ternary_prob(0.5, &mut source_xs);
571    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
572
573    ct_ggsw_rhs.encrypt_sk(
574        module,
575        &pt_ggsw_rhs,
576        &sk_exec,
577        &mut source_xa,
578        &mut source_xe,
579        sigma,
580        scratch.borrow(),
581    );
582
583    ct_ggsw_lhs_in.encrypt_sk(
584        module,
585        &pt_ggsw_lhs,
586        &sk_exec,
587        &mut source_xa,
588        &mut source_xe,
589        sigma,
590        scratch.borrow(),
591    );
592
593    let mut ct_rhs_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank);
594    ct_rhs_exec.prepare(module, &ct_ggsw_rhs, scratch.borrow());
595
596    ct_ggsw_lhs_out.external_product(module, &ct_ggsw_lhs_in, &ct_rhs_exec, scratch.borrow());
597
598    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
599
600    let var_gct_err_lhs: f64 = sigma * sigma;
601    let var_gct_err_rhs: f64 = 0f64;
602
603    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
604    let var_a0_err: f64 = sigma * sigma;
605    let var_a1_err: f64 = 1f64 / 12f64;
606
607    let max_noise = |_col_j: usize| -> f64 {
608        noise_ggsw_product(
609            module.n() as f64,
610            basek * digits,
611            0.5,
612            var_msg,
613            var_a0_err,
614            var_a1_err,
615            var_gct_err_lhs,
616            var_gct_err_rhs,
617            rank as f64,
618            k_in,
619            k_ggsw,
620        ) + 0.5
621    };
622
623    ct_ggsw_lhs_out.assert_noise(module, &sk_exec, &pt_ggsw_lhs, &max_noise);
624}
625
626pub(crate) fn test_external_product_inplace<B: Backend>(
627    module: &Module<B>,
628    basek: usize,
629    k_ct: usize,
630    k_ggsw: usize,
631    digits: usize,
632    rank: usize,
633    sigma: f64,
634) where
635    Module<B>: TestModuleFamily<B>
636        + GGSWAssertNoiseFamily<B>
637        + GGSWKeySwitchFamily<B>
638        + GLWESwitchingKeyEncryptSkFamily<B>
639        + GLWETensorKeyEncryptSkFamily<B>
640        + GGLWEExecLayoutFamily<B>
641        + VecZnxRotateInplace,
642    B: TestScratchFamily<B>,
643{
644    let rows: usize = k_ct.div_ceil(digits * basek);
645    let rows_in: usize = k_ct.div_euclid(basek * digits);
646    let digits_in: usize = 1;
647
648    let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows_in, digits_in, rank);
649    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank);
650
651    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
652    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
653
654    let mut source_xs: Source = Source::new([0u8; 32]);
655    let mut source_xe: Source = Source::new([0u8; 32]);
656    let mut source_xa: Source = Source::new([0u8; 32]);
657
658    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
659
660    let k: usize = 1;
661
662    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
663
664    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
665        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank)
666            | GGSWCiphertext::external_product_inplace_scratch_space(module, basek, k_ct, k_ggsw, digits, rank),
667    );
668
669    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
670    sk.fill_ternary_prob(0.5, &mut source_xs);
671    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
672
673    ct_ggsw_rhs.encrypt_sk(
674        module,
675        &pt_ggsw_rhs,
676        &sk_exec,
677        &mut source_xa,
678        &mut source_xe,
679        sigma,
680        scratch.borrow(),
681    );
682
683    ct_ggsw_lhs.encrypt_sk(
684        module,
685        &pt_ggsw_lhs,
686        &sk_exec,
687        &mut source_xa,
688        &mut source_xe,
689        sigma,
690        scratch.borrow(),
691    );
692
693    let mut ct_rhs_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank);
694    ct_rhs_exec.prepare(module, &ct_ggsw_rhs, scratch.borrow());
695
696    ct_ggsw_lhs.external_product_inplace(module, &ct_rhs_exec, scratch.borrow());
697
698    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
699
700    let var_gct_err_lhs: f64 = sigma * sigma;
701    let var_gct_err_rhs: f64 = 0f64;
702
703    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
704    let var_a0_err: f64 = sigma * sigma;
705    let var_a1_err: f64 = 1f64 / 12f64;
706
707    let max_noise = |_col_j: usize| -> f64 {
708        noise_ggsw_product(
709            module.n() as f64,
710            basek * digits,
711            0.5,
712            var_msg,
713            var_a0_err,
714            var_a1_err,
715            var_gct_err_lhs,
716            var_gct_err_rhs,
717            rank as f64,
718            k_ct,
719            k_ggsw,
720        ) + 0.5
721    };
722
723    ct_ggsw_lhs.assert_noise(module, &sk_exec, &pt_ggsw_lhs, &max_noise);
724}