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    ZnxView, ZnxViewMut, ZnxZero,
  4};
  5use itertools::izip;
  6use sampling::source::Source;
  7
  8use crate::{
  9    automorphism::AutomorphismKey,
 10    elem::Infos,
 11    ggsw_ciphertext::GGSWCiphertext,
 12    glwe_ciphertext::GLWECiphertext,
 13    glwe_ciphertext_fourier::GLWECiphertextFourier,
 14    glwe_plaintext::GLWEPlaintext,
 15    keys::{GLWEPublicKey, SecretKey, SecretKeyFourier},
 16    keyswitch_key::GLWESwitchingKey,
 17    test_fft64::{gglwe::noise_gglwe_product, ggsw::noise_ggsw_product},
 18};
 19
 20#[test]
 21fn encrypt_sk() {
 22    (1..4).for_each(|rank| {
 23        println!("test encrypt_sk rank: {}", rank);
 24        test_encrypt_sk(11, 8, 54, 30, 3.2, rank);
 25    });
 26}
 27
 28#[test]
 29fn encrypt_zero_sk() {
 30    (1..4).for_each(|rank| {
 31        println!("test encrypt_zero_sk rank: {}", rank);
 32        test_encrypt_zero_sk(11, 8, 64, 3.2, rank);
 33    });
 34}
 35
 36#[test]
 37fn encrypt_pk() {
 38    (1..4).for_each(|rank| {
 39        println!("test encrypt_pk rank: {}", rank);
 40        test_encrypt_pk(11, 8, 64, 64, 3.2, rank)
 41    });
 42}
 43
 44#[test]
 45fn keyswitch() {
 46    (1..4).for_each(|rank_in| {
 47        (1..4).for_each(|rank_out| {
 48            println!("test keyswitch rank_in: {} rank_out: {}", rank_in, rank_out);
 49            test_keyswitch(12, 12, 60, 45, 60, rank_in, rank_out, 3.2);
 50        });
 51    });
 52}
 53
 54#[test]
 55fn keyswitch_inplace() {
 56    (1..4).for_each(|rank| {
 57        println!("test keyswitch_inplace rank: {}", rank);
 58        test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
 59    });
 60}
 61
 62#[test]
 63fn external_product() {
 64    (1..4).for_each(|rank| {
 65        println!("test external_product rank: {}", rank);
 66        test_external_product(12, 12, 60, 45, 60, rank, 3.2);
 67    });
 68}
 69
 70#[test]
 71fn external_product_inplace() {
 72    (1..4).for_each(|rank| {
 73        println!("test external_product rank: {}", rank);
 74        test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
 75    });
 76}
 77
 78fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) {
 79    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
 80
 81    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
 82    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_pt);
 83
 84    let mut source_xs: Source = Source::new([0u8; 32]);
 85    let mut source_xe: Source = Source::new([0u8; 32]);
 86    let mut source_xa: Source = Source::new([0u8; 32]);
 87
 88    let mut scratch: ScratchOwned = ScratchOwned::new(
 89        GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size()) | GLWECiphertext::decrypt_scratch_space(&module, ct.size()),
 90    );
 91
 92    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
 93    sk.fill_ternary_prob(0.5, &mut source_xs);
 94
 95    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
 96    sk_dft.dft(&module, &sk);
 97
 98    let mut data_want: Vec<i64> = vec![0i64; module.n()];
 99
100    data_want
101        .iter_mut()
102        .for_each(|x| *x = source_xa.next_i64() & 0xFF);
103
104    pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10);
105
106    ct.encrypt_sk(
107        &module,
108        &pt,
109        &sk_dft,
110        &mut source_xa,
111        &mut source_xe,
112        sigma,
113        scratch.borrow(),
114    );
115
116    pt.data.zero();
117
118    ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
119
120    let mut data_have: Vec<i64> = vec![0i64; module.n()];
121
122    pt.data
123        .decode_vec_i64(0, basek, pt.size() * basek, &mut data_have);
124
125    // TODO: properly assert the decryption noise through std(dec(ct) - pt)
126    let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64;
127    izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
128        let b_scaled = (*b as f64) / scale;
129        assert!(
130            (*a as f64 - b_scaled).abs() < 0.1,
131            "{} {}",
132            *a as f64,
133            b_scaled
134        )
135    });
136}
137
138fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) {
139    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
140
141    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
142
143    let mut source_xs: Source = Source::new([0u8; 32]);
144    let mut source_xe: Source = Source::new([1u8; 32]);
145    let mut source_xa: Source = Source::new([0u8; 32]);
146
147    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
148    sk.fill_ternary_prob(0.5, &mut source_xs);
149    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
150    sk_dft.dft(&module, &sk);
151
152    let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ct, rank);
153
154    let mut scratch: ScratchOwned = ScratchOwned::new(
155        GLWECiphertextFourier::decrypt_scratch_space(&module, ct_dft.size())
156            | GLWECiphertextFourier::encrypt_sk_scratch_space(&module, rank, ct_dft.size()),
157    );
158
159    ct_dft.encrypt_zero_sk(
160        &module,
161        &sk_dft,
162        &mut source_xa,
163        &mut source_xe,
164        sigma,
165        scratch.borrow(),
166    );
167    ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
168
169    assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2);
170}
171
172fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) {
173    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
174
175    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
176    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
177
178    let mut source_xs: Source = Source::new([0u8; 32]);
179    let mut source_xe: Source = Source::new([0u8; 32]);
180    let mut source_xa: Source = Source::new([0u8; 32]);
181    let mut source_xu: Source = Source::new([0u8; 32]);
182
183    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
184    sk.fill_ternary_prob(0.5, &mut source_xs);
185    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
186    sk_dft.dft(&module, &sk);
187
188    let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::new(&module, basek, k_pk, rank);
189    pk.generate(&module, &sk_dft, &mut source_xa, &mut source_xe, sigma);
190
191    let mut scratch: ScratchOwned = ScratchOwned::new(
192        GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
193            | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
194            | GLWECiphertext::encrypt_pk_scratch_space(&module, pk.size()),
195    );
196
197    let mut data_want: Vec<i64> = vec![0i64; module.n()];
198
199    data_want
200        .iter_mut()
201        .for_each(|x| *x = source_xa.next_i64() & 0);
202
203    pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10);
204
205    ct.encrypt_pk(
206        &module,
207        &pt_want,
208        &pk,
209        &mut source_xu,
210        &mut source_xe,
211        sigma,
212        scratch.borrow(),
213    );
214
215    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
216
217    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
218
219    module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
220
221    let noise_have: f64 = pt_want.data.std(0, basek).log2();
222    let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64);
223
224    assert!(
225        (noise_have - noise_want).abs() < 0.2,
226        "{} {}",
227        noise_have,
228        noise_want
229    );
230}
231
232fn test_keyswitch(
233    log_n: usize,
234    basek: usize,
235    k_keyswitch: usize,
236    k_ct_in: usize,
237    k_ct_out: usize,
238    rank_in: usize,
239    rank_out: usize,
240    sigma: f64,
241) {
242    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
243    let rows: usize = (k_ct_in + basek - 1) / basek;
244
245    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::new(&module, basek, k_keyswitch, rows, rank_in, rank_out);
246    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank_in);
247    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank_out);
248    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
249    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
250
251    let mut source_xs: Source = Source::new([0u8; 32]);
252    let mut source_xe: Source = Source::new([0u8; 32]);
253    let mut source_xa: Source = Source::new([0u8; 32]);
254
255    // Random input plaintext
256    pt_want
257        .data
258        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
259
260    let mut scratch: ScratchOwned = ScratchOwned::new(
261        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank_in, ksk.size())
262            | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
263            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
264            | GLWECiphertext::keyswitch_scratch_space(
265                &module,
266                ct_out.size(),
267                rank_out,
268                ct_in.size(),
269                rank_in,
270                ksk.size(),
271            ),
272    );
273
274    let mut sk_in: SecretKey<Vec<u8>> = SecretKey::new(&module, rank_in);
275    sk_in.fill_ternary_prob(0.5, &mut source_xs);
276
277    let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank_in);
278    sk_in_dft.dft(&module, &sk_in);
279
280    let mut sk_out: SecretKey<Vec<u8>> = SecretKey::new(&module, rank_out);
281    sk_out.fill_ternary_prob(0.5, &mut source_xs);
282
283    let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank_out);
284    sk_out_dft.dft(&module, &sk_out);
285
286    ksk.encrypt_sk(
287        &module,
288        &sk_in,
289        &sk_out_dft,
290        &mut source_xa,
291        &mut source_xe,
292        sigma,
293        scratch.borrow(),
294    );
295
296    ct_in.encrypt_sk(
297        &module,
298        &pt_want,
299        &sk_in_dft,
300        &mut source_xa,
301        &mut source_xe,
302        sigma,
303        scratch.borrow(),
304    );
305
306    ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
307
308    ct_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
309
310    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
311
312    let noise_have: f64 = pt_have.data.std(0, basek).log2();
313    let noise_want: f64 = noise_gglwe_product(
314        module.n() as f64,
315        basek,
316        0.5,
317        0.5,
318        0f64,
319        sigma * sigma,
320        0f64,
321        rank_in as f64,
322        k_ct_in,
323        k_keyswitch,
324    );
325
326    assert!(
327        (noise_have - noise_want).abs() <= 0.1,
328        "{} {}",
329        noise_have,
330        noise_want
331    );
332}
333
334fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize, rank: usize, sigma: f64) {
335    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
336    let rows: usize = (k_ct + basek - 1) / basek;
337
338    let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::new(&module, basek, k_ksk, rows, rank, rank);
339    let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
340    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
341    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
342
343    let mut source_xs: Source = Source::new([0u8; 32]);
344    let mut source_xe: Source = Source::new([0u8; 32]);
345    let mut source_xa: Source = Source::new([0u8; 32]);
346
347    // Random input plaintext
348    pt_want
349        .data
350        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
351
352    let mut scratch: ScratchOwned = ScratchOwned::new(
353        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe.size())
354            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
355            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
356            | GLWECiphertext::keyswitch_inplace_scratch_space(&module, ct_rlwe.size(), rank, ct_grlwe.size()),
357    );
358
359    let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
360    sk0.fill_ternary_prob(0.5, &mut source_xs);
361
362    let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
363    sk0_dft.dft(&module, &sk0);
364
365    let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
366    sk1.fill_ternary_prob(0.5, &mut source_xs);
367
368    let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
369    sk1_dft.dft(&module, &sk1);
370
371    ct_grlwe.encrypt_sk(
372        &module,
373        &sk0,
374        &sk1_dft,
375        &mut source_xa,
376        &mut source_xe,
377        sigma,
378        scratch.borrow(),
379    );
380
381    ct_rlwe.encrypt_sk(
382        &module,
383        &pt_want,
384        &sk0_dft,
385        &mut source_xa,
386        &mut source_xe,
387        sigma,
388        scratch.borrow(),
389    );
390
391    ct_rlwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
392
393    ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
394
395    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
396
397    let noise_have: f64 = pt_have.data.std(0, basek).log2();
398    let noise_want: f64 = noise_gglwe_product(
399        module.n() as f64,
400        basek,
401        0.5,
402        0.5,
403        0f64,
404        sigma * sigma,
405        0f64,
406        rank as f64,
407        k_ct,
408        k_ksk,
409    );
410
411    assert!(
412        (noise_have - noise_want).abs() <= 0.1,
413        "{} {}",
414        noise_have,
415        noise_want
416    );
417}
418
419#[test]
420fn automorphism() {
421    (1..4).for_each(|rank| {
422        println!("test automorphism rank: {}", rank);
423        test_automorphism(12, 12, -5, 60, 45, 60, rank, 3.2);
424    });
425}
426
427fn test_automorphism(
428    log_n: usize,
429    basek: usize,
430    p: i64,
431    k_autokey: usize,
432    k_ct_in: usize,
433    k_ct_out: usize,
434    rank: usize,
435    sigma: f64,
436) {
437    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
438    let rows: usize = (k_ct_in + basek - 1) / basek;
439
440    let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k_autokey, rows, rank);
441    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank);
442    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank);
443    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
444    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
445
446    let mut source_xs: Source = Source::new([0u8; 32]);
447    let mut source_xe: Source = Source::new([0u8; 32]);
448    let mut source_xa: Source = Source::new([0u8; 32]);
449
450    pt_want
451        .data
452        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
453
454    let mut scratch: ScratchOwned = ScratchOwned::new(
455        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
456            | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
457            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
458            | GLWECiphertext::automorphism_scratch_space(&module, ct_out.size(), rank, ct_in.size(), autokey.size()),
459    );
460
461    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
462    sk.fill_ternary_prob(0.5, &mut source_xs);
463
464    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
465    sk_dft.dft(&module, &sk);
466
467    autokey.encrypt_sk(
468        &module,
469        p,
470        &sk,
471        &mut source_xa,
472        &mut source_xe,
473        sigma,
474        scratch.borrow(),
475    );
476
477    ct_in.encrypt_sk(
478        &module,
479        &pt_want,
480        &sk_dft,
481        &mut source_xa,
482        &mut source_xe,
483        sigma,
484        scratch.borrow(),
485    );
486
487    ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
488    ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
489    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
490    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
491    module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow());
492
493    let noise_have: f64 = pt_have.data.std(0, basek).log2();
494
495    println!("{}", noise_have);
496
497    let noise_want: f64 = noise_gglwe_product(
498        module.n() as f64,
499        basek,
500        0.5,
501        0.5,
502        0f64,
503        sigma * sigma,
504        0f64,
505        rank as f64,
506        k_ct_in,
507        k_autokey,
508    );
509
510    assert!(
511        (noise_have - noise_want).abs() <= 0.1,
512        "{} {}",
513        noise_have,
514        noise_want
515    );
516}
517
518#[test]
519fn automorphism_inplace() {
520    (1..4).for_each(|rank| {
521        println!("test automorphism_inplace rank: {}", rank);
522        test_automorphism_inplace(12, 12, -5, 60, 60, rank, 3.2);
523    });
524}
525
526fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usize, k_ct: usize, rank: usize, sigma: f64) {
527    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
528    let rows: usize = (k_ct + basek - 1) / basek;
529
530    let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k_autokey, rows, rank);
531    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
532    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
533    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
534
535    let mut source_xs: Source = Source::new([0u8; 32]);
536    let mut source_xe: Source = Source::new([0u8; 32]);
537    let mut source_xa: Source = Source::new([0u8; 32]);
538
539    // Random input plaintext
540    pt_want
541        .data
542        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
543
544    let mut scratch: ScratchOwned = ScratchOwned::new(
545        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
546            | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
547            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
548            | GLWECiphertext::automorphism_inplace_scratch_space(&module, ct.size(), rank, autokey.size()),
549    );
550
551    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
552    sk.fill_ternary_prob(0.5, &mut source_xs);
553
554    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
555    sk_dft.dft(&module, &sk);
556
557    autokey.encrypt_sk(
558        &module,
559        p,
560        &sk,
561        &mut source_xa,
562        &mut source_xe,
563        sigma,
564        scratch.borrow(),
565    );
566
567    ct.encrypt_sk(
568        &module,
569        &pt_want,
570        &sk_dft,
571        &mut source_xa,
572        &mut source_xe,
573        sigma,
574        scratch.borrow(),
575    );
576
577    ct.automorphism_inplace(&module, &autokey, scratch.borrow());
578    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
579    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
580    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
581    module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow());
582
583    let noise_have: f64 = pt_have.data.std(0, basek).log2();
584    let noise_want: f64 = noise_gglwe_product(
585        module.n() as f64,
586        basek,
587        0.5,
588        0.5,
589        0f64,
590        sigma * sigma,
591        0f64,
592        rank as f64,
593        k_ct,
594        k_autokey,
595    );
596
597    assert!(
598        (noise_have - noise_want).abs() <= 0.1,
599        "{} {}",
600        noise_have,
601        noise_want
602    );
603}
604
605fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
606    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
607
608    let rows: usize = (k_ct_in + basek - 1) / basek;
609
610    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
611    let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank);
612    let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank);
613    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
614    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
615    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
616
617    let mut source_xs: Source = Source::new([0u8; 32]);
618    let mut source_xe: Source = Source::new([0u8; 32]);
619    let mut source_xa: Source = Source::new([0u8; 32]);
620
621    // Random input plaintext
622    pt_want
623        .data
624        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
625
626    pt_want.to_mut().at_mut(0, 0)[1] = 1;
627
628    let k: usize = 1;
629
630    pt_rgsw.raw_mut()[k] = 1; // X^{k}
631
632    let mut scratch: ScratchOwned = ScratchOwned::new(
633        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
634            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
635            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
636            | GLWECiphertext::external_product_scratch_space(
637                &module,
638                ct_rlwe_out.size(),
639                ct_rlwe_in.size(),
640                ct_rgsw.size(),
641                rank,
642            ),
643    );
644
645    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
646    sk.fill_ternary_prob(0.5, &mut source_xs);
647
648    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
649    sk_dft.dft(&module, &sk);
650
651    ct_rgsw.encrypt_sk(
652        &module,
653        &pt_rgsw,
654        &sk_dft,
655        &mut source_xa,
656        &mut source_xe,
657        sigma,
658        scratch.borrow(),
659    );
660
661    ct_rlwe_in.encrypt_sk(
662        &module,
663        &pt_want,
664        &sk_dft,
665        &mut source_xa,
666        &mut source_xe,
667        sigma,
668        scratch.borrow(),
669    );
670
671    ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
672
673    ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
674
675    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
676
677    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
678
679    let noise_have: f64 = pt_have.data.std(0, basek).log2();
680
681    let var_gct_err_lhs: f64 = sigma * sigma;
682    let var_gct_err_rhs: f64 = 0f64;
683
684    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
685    let var_a0_err: f64 = sigma * sigma;
686    let var_a1_err: f64 = 1f64 / 12f64;
687
688    let noise_want: f64 = noise_ggsw_product(
689        module.n() as f64,
690        basek,
691        0.5,
692        var_msg,
693        var_a0_err,
694        var_a1_err,
695        var_gct_err_lhs,
696        var_gct_err_rhs,
697        rank as f64,
698        k_ct_in,
699        k_ggsw,
700    );
701
702    assert!(
703        (noise_have - noise_want).abs() <= 0.1,
704        "{} {}",
705        noise_have,
706        noise_want
707    );
708}
709
710fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
711    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
712    let rows: usize = (k_ct + basek - 1) / basek;
713
714    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
715    let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
716    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
717    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
718    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
719
720    let mut source_xs: Source = Source::new([0u8; 32]);
721    let mut source_xe: Source = Source::new([0u8; 32]);
722    let mut source_xa: Source = Source::new([0u8; 32]);
723
724    // Random input plaintext
725    pt_want
726        .data
727        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
728
729    pt_want.to_mut().at_mut(0, 0)[1] = 1;
730
731    let k: usize = 1;
732
733    pt_rgsw.raw_mut()[k] = 1; // X^{k}
734
735    let mut scratch: ScratchOwned = ScratchOwned::new(
736        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
737            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
738            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
739            | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size(), rank),
740    );
741
742    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
743    sk.fill_ternary_prob(0.5, &mut source_xs);
744
745    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
746    sk_dft.dft(&module, &sk);
747
748    ct_rgsw.encrypt_sk(
749        &module,
750        &pt_rgsw,
751        &sk_dft,
752        &mut source_xa,
753        &mut source_xe,
754        sigma,
755        scratch.borrow(),
756    );
757
758    ct_rlwe.encrypt_sk(
759        &module,
760        &pt_want,
761        &sk_dft,
762        &mut source_xa,
763        &mut source_xe,
764        sigma,
765        scratch.borrow(),
766    );
767
768    ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
769
770    ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
771
772    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
773
774    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
775
776    let noise_have: f64 = pt_have.data.std(0, basek).log2();
777
778    let var_gct_err_lhs: f64 = sigma * sigma;
779    let var_gct_err_rhs: f64 = 0f64;
780
781    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
782    let var_a0_err: f64 = sigma * sigma;
783    let var_a1_err: f64 = 1f64 / 12f64;
784
785    let noise_want: f64 = noise_ggsw_product(
786        module.n() as f64,
787        basek,
788        0.5,
789        var_msg,
790        var_a0_err,
791        var_a1_err,
792        var_gct_err_lhs,
793        var_gct_err_rhs,
794        rank as f64,
795        k_ct,
796        k_ggsw,
797    );
798
799    assert!(
800        (noise_have - noise_want).abs() <= 0.1,
801        "{} {}",
802        noise_have,
803        noise_want
804    );
805}