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    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, 1, 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    // Random input plaintext
451    pt_want
452        .data
453        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
454
455    let mut scratch: ScratchOwned = ScratchOwned::new(
456        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
457            | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
458            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
459            | GLWECiphertext::automorphism_scratch_space(&module, ct_out.size(), rank, ct_in.size(), autokey.size()),
460    );
461
462    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
463    sk.fill_ternary_prob(0.5, &mut source_xs);
464
465    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
466    sk_dft.dft(&module, &sk);
467
468    autokey.encrypt_sk(
469        &module,
470        p,
471        &sk,
472        &mut source_xa,
473        &mut source_xe,
474        sigma,
475        scratch.borrow(),
476    );
477
478    ct_in.encrypt_sk(
479        &module,
480        &pt_want,
481        &sk_dft,
482        &mut source_xa,
483        &mut source_xe,
484        sigma,
485        scratch.borrow(),
486    );
487
488    ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
489
490    ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
491
492    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
493
494    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
495
496    let noise_have: f64 = pt_have.data.std(0, basek).log2();
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, 1, 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
579    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
580
581    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
582
583    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
584
585    let noise_have: f64 = pt_have.data.std(0, basek).log2();
586    let noise_want: f64 = noise_gglwe_product(
587        module.n() as f64,
588        basek,
589        0.5,
590        0.5,
591        0f64,
592        sigma * sigma,
593        0f64,
594        rank as f64,
595        k_ct,
596        k_autokey,
597    );
598
599    assert!(
600        (noise_have - noise_want).abs() <= 0.1,
601        "{} {}",
602        noise_have,
603        noise_want
604    );
605}
606
607fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
608    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
609
610    let rows: usize = (k_ct_in + basek - 1) / basek;
611
612    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
613    let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank);
614    let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank);
615    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
616    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
617    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
618
619    let mut source_xs: Source = Source::new([0u8; 32]);
620    let mut source_xe: Source = Source::new([0u8; 32]);
621    let mut source_xa: Source = Source::new([0u8; 32]);
622
623    // Random input plaintext
624    pt_want
625        .data
626        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
627
628    pt_want.to_mut().at_mut(0, 0)[1] = 1;
629
630    let k: usize = 1;
631
632    pt_rgsw.raw_mut()[k] = 1; // X^{k}
633
634    let mut scratch: ScratchOwned = ScratchOwned::new(
635        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
636            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
637            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
638            | GLWECiphertext::external_product_scratch_space(
639                &module,
640                ct_rlwe_out.size(),
641                ct_rlwe_in.size(),
642                ct_rgsw.size(),
643                rank,
644            ),
645    );
646
647    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
648    sk.fill_ternary_prob(0.5, &mut source_xs);
649
650    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
651    sk_dft.dft(&module, &sk);
652
653    ct_rgsw.encrypt_sk(
654        &module,
655        &pt_rgsw,
656        &sk_dft,
657        &mut source_xa,
658        &mut source_xe,
659        sigma,
660        scratch.borrow(),
661    );
662
663    ct_rlwe_in.encrypt_sk(
664        &module,
665        &pt_want,
666        &sk_dft,
667        &mut source_xa,
668        &mut source_xe,
669        sigma,
670        scratch.borrow(),
671    );
672
673    ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
674
675    ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
676
677    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
678
679    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
680
681    let noise_have: f64 = pt_have.data.std(0, basek).log2();
682
683    let var_gct_err_lhs: f64 = sigma * sigma;
684    let var_gct_err_rhs: f64 = 0f64;
685
686    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
687    let var_a0_err: f64 = sigma * sigma;
688    let var_a1_err: f64 = 1f64 / 12f64;
689
690    let noise_want: f64 = noise_ggsw_product(
691        module.n() as f64,
692        basek,
693        0.5,
694        var_msg,
695        var_a0_err,
696        var_a1_err,
697        var_gct_err_lhs,
698        var_gct_err_rhs,
699        rank as f64,
700        k_ct_in,
701        k_ggsw,
702    );
703
704    assert!(
705        (noise_have - noise_want).abs() <= 0.1,
706        "{} {}",
707        noise_have,
708        noise_want
709    );
710}
711
712fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
713    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
714    let rows: usize = (k_ct + basek - 1) / basek;
715
716    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
717    let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
718    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
719    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
720    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
721
722    let mut source_xs: Source = Source::new([0u8; 32]);
723    let mut source_xe: Source = Source::new([0u8; 32]);
724    let mut source_xa: Source = Source::new([0u8; 32]);
725
726    // Random input plaintext
727    pt_want
728        .data
729        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
730
731    pt_want.to_mut().at_mut(0, 0)[1] = 1;
732
733    let k: usize = 1;
734
735    pt_rgsw.raw_mut()[k] = 1; // X^{k}
736
737    let mut scratch: ScratchOwned = ScratchOwned::new(
738        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
739            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
740            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
741            | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size(), rank),
742    );
743
744    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
745    sk.fill_ternary_prob(0.5, &mut source_xs);
746
747    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
748    sk_dft.dft(&module, &sk);
749
750    ct_rgsw.encrypt_sk(
751        &module,
752        &pt_rgsw,
753        &sk_dft,
754        &mut source_xa,
755        &mut source_xe,
756        sigma,
757        scratch.borrow(),
758    );
759
760    ct_rlwe.encrypt_sk(
761        &module,
762        &pt_want,
763        &sk_dft,
764        &mut source_xa,
765        &mut source_xe,
766        sigma,
767        scratch.borrow(),
768    );
769
770    ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
771
772    ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
773
774    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
775
776    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
777
778    let noise_have: f64 = pt_have.data.std(0, basek).log2();
779
780    let var_gct_err_lhs: f64 = sigma * sigma;
781    let var_gct_err_rhs: f64 = 0f64;
782
783    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
784    let var_a0_err: f64 = sigma * sigma;
785    let var_a1_err: f64 = 1f64 / 12f64;
786
787    let noise_want: f64 = noise_ggsw_product(
788        module.n() as f64,
789        basek,
790        0.5,
791        var_msg,
792        var_a0_err,
793        var_a1_err,
794        var_gct_err_lhs,
795        var_gct_err_rhs,
796        rank as f64,
797        k_ct,
798        k_ggsw,
799    );
800
801    assert!(
802        (noise_have - noise_want).abs() <= 0.1,
803        "{} {}",
804        noise_have,
805        noise_want
806    );
807}