arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/grlwe.rs
core/src/test_fft64/grlwe.rsRaw
  1#[cfg(test)]
  2
  3mod tests {
  4    use base2k::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps};
  5    use sampling::source::Source;
  6
  7    use crate::{
  8        elem::Infos,
  9        grlwe::GRLWECt,
 10        keys::{SecretKey, SecretKeyDft},
 11        rlwe::{RLWECt, RLWECtDft, RLWEPt},
 12        test_fft64::grlwe::noise_grlwe_rlwe_product,
 13    };
 14
 15    #[test]
 16    fn encrypt_sk() {
 17        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 18        let log_base2k: usize = 8;
 19        let log_k_ct: usize = 54;
 20        let rows: usize = 4;
 21
 22        let sigma: f64 = 3.2;
 23        let bound: f64 = sigma * 6.0;
 24
 25        let mut ct: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_ct, rows);
 26        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
 27        let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
 28
 29        let mut source_xs: Source = Source::new([0u8; 32]);
 30        let mut source_xe: Source = Source::new([0u8; 32]);
 31        let mut source_xa: Source = Source::new([0u8; 32]);
 32
 33        pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 34
 35        let mut scratch: ScratchOwned = ScratchOwned::new(
 36            GRLWECt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
 37        );
 38
 39        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
 40        sk.fill_ternary_prob(0.5, &mut source_xs);
 41
 42        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
 43        sk_dft.dft(&module, &sk);
 44
 45        ct.encrypt_sk(
 46            &module,
 47            &pt_scalar,
 48            &sk_dft,
 49            &mut source_xa,
 50            &mut source_xe,
 51            sigma,
 52            bound,
 53            scratch.borrow(),
 54        );
 55
 56        let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
 57
 58        (0..ct.rows()).for_each(|row_i| {
 59            ct.get_row(&module, row_i, &mut ct_rlwe_dft);
 60            ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
 61            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_scalar, 0);
 62            let std_pt: f64 = pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
 63            assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
 64        });
 65
 66        module.free();
 67    }
 68
 69    #[test]
 70    fn mul_rlwe() {
 71        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 72        let log_base2k: usize = 12;
 73        let log_k_grlwe: usize = 60;
 74        let log_k_rlwe_in: usize = 45;
 75        let log_k_rlwe_out: usize = 60;
 76        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
 77
 78        let sigma: f64 = 3.2;
 79        let bound: f64 = sigma * 6.0;
 80
 81        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
 82        let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
 83        let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
 84        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
 85        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
 86
 87        let mut source_xs: Source = Source::new([0u8; 32]);
 88        let mut source_xe: Source = Source::new([0u8; 32]);
 89        let mut source_xa: Source = Source::new([0u8; 32]);
 90
 91        // Random input plaintext
 92        pt_want
 93            .data
 94            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
 95
 96        let mut scratch: ScratchOwned = ScratchOwned::new(
 97            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
 98                | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
 99                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
100                | GRLWECt::mul_rlwe_scratch_space(
101                    &module,
102                    ct_rlwe_out.size(),
103                    ct_rlwe_in.size(),
104                    ct_grlwe.size(),
105                ),
106        );
107
108        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
109        sk0.fill_ternary_prob(0.5, &mut source_xs);
110
111        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
112        sk0_dft.dft(&module, &sk0);
113
114        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
115        sk1.fill_ternary_prob(0.5, &mut source_xs);
116
117        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
118        sk1_dft.dft(&module, &sk1);
119
120        ct_grlwe.encrypt_sk(
121            &module,
122            &sk0.data,
123            &sk1_dft,
124            &mut source_xa,
125            &mut source_xe,
126            sigma,
127            bound,
128            scratch.borrow(),
129        );
130
131        ct_rlwe_in.encrypt_sk(
132            &module,
133            Some(&pt_want),
134            &sk0_dft,
135            &mut source_xa,
136            &mut source_xe,
137            sigma,
138            bound,
139            scratch.borrow(),
140        );
141
142        ct_grlwe.mul_rlwe(&module, &mut ct_rlwe_out, &ct_rlwe_in, scratch.borrow());
143
144        ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
145
146        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
147
148        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
149        let noise_want: f64 = noise_grlwe_rlwe_product(
150            module.n() as f64,
151            log_base2k,
152            0.5,
153            0.5,
154            0f64,
155            sigma * sigma,
156            0f64,
157            log_k_rlwe_in,
158            log_k_grlwe,
159        );
160
161        assert!(
162            (noise_have - noise_want).abs() <= 0.1,
163            "{} {}",
164            noise_have,
165            noise_want
166        );
167
168        module.free();
169    }
170
171    #[test]
172    fn mul_rlwe_inplace() {
173        let module: Module<FFT64> = Module::<FFT64>::new(2048);
174        let log_base2k: usize = 12;
175        let log_k_grlwe: usize = 60;
176        let log_k_rlwe: usize = 45;
177        let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
178
179        let sigma: f64 = 3.2;
180        let bound: f64 = sigma * 6.0;
181
182        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
183        let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
184        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
185        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
186
187        let mut source_xs: Source = Source::new([0u8; 32]);
188        let mut source_xe: Source = Source::new([0u8; 32]);
189        let mut source_xa: Source = Source::new([0u8; 32]);
190
191        // Random input plaintext
192        pt_want
193            .data
194            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
195
196        let mut scratch: ScratchOwned = ScratchOwned::new(
197            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
198                | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
199                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
200                | GRLWECt::mul_rlwe_scratch_space(&module, ct_rlwe.size(), ct_rlwe.size(), ct_grlwe.size()),
201        );
202
203        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
204        sk0.fill_ternary_prob(0.5, &mut source_xs);
205
206        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
207        sk0_dft.dft(&module, &sk0);
208
209        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
210        sk1.fill_ternary_prob(0.5, &mut source_xs);
211
212        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
213        sk1_dft.dft(&module, &sk1);
214
215        ct_grlwe.encrypt_sk(
216            &module,
217            &sk0.data,
218            &sk1_dft,
219            &mut source_xa,
220            &mut source_xe,
221            sigma,
222            bound,
223            scratch.borrow(),
224        );
225
226        ct_rlwe.encrypt_sk(
227            &module,
228            Some(&pt_want),
229            &sk0_dft,
230            &mut source_xa,
231            &mut source_xe,
232            sigma,
233            bound,
234            scratch.borrow(),
235        );
236
237        ct_grlwe.mul_rlwe_inplace(&module, &mut ct_rlwe, scratch.borrow());
238
239        ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
240
241        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
242
243        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
244        let noise_want: f64 = noise_grlwe_rlwe_product(
245            module.n() as f64,
246            log_base2k,
247            0.5,
248            0.5,
249            0f64,
250            sigma * sigma,
251            0f64,
252            log_k_rlwe,
253            log_k_grlwe,
254        );
255
256        assert!(
257            (noise_have - noise_want).abs() <= 0.1,
258            "{} {}",
259            noise_have,
260            noise_want
261        );
262
263        module.free();
264    }
265
266    #[test]
267    fn mul_rlwe_dft() {
268        let module: Module<FFT64> = Module::<FFT64>::new(2048);
269        let log_base2k: usize = 12;
270        let log_k_grlwe: usize = 60;
271        let log_k_rlwe_in: usize = 45;
272        let log_k_rlwe_out: usize = 60;
273        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
274
275        let sigma: f64 = 3.2;
276        let bound: f64 = sigma * 6.0;
277
278        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
279        let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
280        let mut ct_rlwe_in_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_in);
281        let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
282        let mut ct_rlwe_out_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_out);
283        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
284        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
285
286        let mut source_xs: Source = Source::new([0u8; 32]);
287        let mut source_xe: Source = Source::new([0u8; 32]);
288        let mut source_xa: Source = Source::new([0u8; 32]);
289
290        // Random input plaintext
291        pt_want
292            .data
293            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
294
295        let mut scratch: ScratchOwned = ScratchOwned::new(
296            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
297                | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
298                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
299                | GRLWECt::mul_rlwe_scratch_space(
300                    &module,
301                    ct_rlwe_out.size(),
302                    ct_rlwe_in.size(),
303                    ct_grlwe.size(),
304                ),
305        );
306
307        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
308        sk0.fill_ternary_prob(0.5, &mut source_xs);
309
310        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
311        sk0_dft.dft(&module, &sk0);
312
313        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
314        sk1.fill_ternary_prob(0.5, &mut source_xs);
315
316        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
317        sk1_dft.dft(&module, &sk1);
318
319        ct_grlwe.encrypt_sk(
320            &module,
321            &sk0.data,
322            &sk1_dft,
323            &mut source_xa,
324            &mut source_xe,
325            sigma,
326            bound,
327            scratch.borrow(),
328        );
329
330        ct_rlwe_in.encrypt_sk(
331            &module,
332            Some(&pt_want),
333            &sk0_dft,
334            &mut source_xa,
335            &mut source_xe,
336            sigma,
337            bound,
338            scratch.borrow(),
339        );
340
341        ct_rlwe_in.dft(&module, &mut ct_rlwe_in_dft);
342        ct_grlwe.mul_rlwe_dft(
343            &module,
344            &mut ct_rlwe_out_dft,
345            &ct_rlwe_in_dft,
346            scratch.borrow(),
347        );
348        ct_rlwe_out_dft.idft(&module, &mut ct_rlwe_out, scratch.borrow());
349
350        ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
351
352        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
353
354        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
355        let noise_want: f64 = noise_grlwe_rlwe_product(
356            module.n() as f64,
357            log_base2k,
358            0.5,
359            0.5,
360            0f64,
361            sigma * sigma,
362            0f64,
363            log_k_rlwe_in,
364            log_k_grlwe,
365        );
366
367        assert!(
368            (noise_have - noise_want).abs() <= 0.1,
369            "{} {}",
370            noise_have,
371            noise_want
372        );
373
374        module.free();
375    }
376
377    #[test]
378    fn mul_rlwe_dft_inplace() {
379        let module: Module<FFT64> = Module::<FFT64>::new(2048);
380        let log_base2k: usize = 12;
381        let log_k_grlwe: usize = 60;
382        let log_k_rlwe: usize = 45;
383        let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
384
385        let sigma: f64 = 3.2;
386        let bound: f64 = sigma * 6.0;
387
388        let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
389        let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
390        let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe);
391        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
392        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
393
394        let mut source_xs: Source = Source::new([0u8; 32]);
395        let mut source_xe: Source = Source::new([0u8; 32]);
396        let mut source_xa: Source = Source::new([0u8; 32]);
397
398        // Random input plaintext
399        pt_want
400            .data
401            .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
402
403        let mut scratch: ScratchOwned = ScratchOwned::new(
404            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
405                | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
406                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
407                | GRLWECt::mul_rlwe_scratch_space(&module, ct_rlwe.size(), ct_rlwe.size(), ct_grlwe.size()),
408        );
409
410        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
411        sk0.fill_ternary_prob(0.5, &mut source_xs);
412
413        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
414        sk0_dft.dft(&module, &sk0);
415
416        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
417        sk1.fill_ternary_prob(0.5, &mut source_xs);
418
419        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
420        sk1_dft.dft(&module, &sk1);
421
422        ct_grlwe.encrypt_sk(
423            &module,
424            &sk0.data,
425            &sk1_dft,
426            &mut source_xa,
427            &mut source_xe,
428            sigma,
429            bound,
430            scratch.borrow(),
431        );
432
433        ct_rlwe.encrypt_sk(
434            &module,
435            Some(&pt_want),
436            &sk0_dft,
437            &mut source_xa,
438            &mut source_xe,
439            sigma,
440            bound,
441            scratch.borrow(),
442        );
443
444        ct_rlwe.dft(&module, &mut ct_rlwe_dft);
445        ct_grlwe.mul_rlwe_dft_inplace(&module, &mut ct_rlwe_dft, scratch.borrow());
446        ct_rlwe_dft.idft(&module, &mut ct_rlwe, scratch.borrow());
447
448        ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
449
450        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
451
452        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
453        let noise_want: f64 = noise_grlwe_rlwe_product(
454            module.n() as f64,
455            log_base2k,
456            0.5,
457            0.5,
458            0f64,
459            sigma * sigma,
460            0f64,
461            log_k_rlwe,
462            log_k_grlwe,
463        );
464
465        assert!(
466            (noise_have - noise_want).abs() <= 0.1,
467            "{} {}",
468            noise_have,
469            noise_want
470        );
471
472        module.free();
473    }
474
475    #[test]
476    fn mul_grlwe() {
477        let module: Module<FFT64> = Module::<FFT64>::new(2048);
478        let log_base2k: usize = 12;
479        let log_k_grlwe: usize = 60;
480        let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
481
482        let sigma: f64 = 3.2;
483        let bound: f64 = sigma * 6.0;
484
485        let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
486        let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
487        let mut ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
488
489        let mut source_xs: Source = Source::new([0u8; 32]);
490        let mut source_xe: Source = Source::new([0u8; 32]);
491        let mut source_xa: Source = Source::new([0u8; 32]);
492
493        let mut scratch: ScratchOwned = ScratchOwned::new(
494            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
495                | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s2.size())
496                | GRLWECt::mul_grlwe_scratch_space(
497                    &module,
498                    ct_grlwe_s0s2.size(),
499                    ct_grlwe_s0s1.size(),
500                    ct_grlwe_s1s2.size(),
501                ),
502        );
503
504        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
505        sk0.fill_ternary_prob(0.5, &mut source_xs);
506
507        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
508        sk0_dft.dft(&module, &sk0);
509
510        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
511        sk1.fill_ternary_prob(0.5, &mut source_xs);
512
513        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
514        sk1_dft.dft(&module, &sk1);
515
516        let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
517        sk2.fill_ternary_prob(0.5, &mut source_xs);
518
519        let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
520        sk2_dft.dft(&module, &sk2);
521
522        // GRLWE_{s1}(s0) = s0 -> s1
523        ct_grlwe_s0s1.encrypt_sk(
524            &module,
525            &sk0.data,
526            &sk1_dft,
527            &mut source_xa,
528            &mut source_xe,
529            sigma,
530            bound,
531            scratch.borrow(),
532        );
533
534        // GRLWE_{s2}(s1) -> s1 -> s2
535        ct_grlwe_s1s2.encrypt_sk(
536            &module,
537            &sk1.data,
538            &sk2_dft,
539            &mut source_xa,
540            &mut source_xe,
541            sigma,
542            bound,
543            scratch.borrow(),
544        );
545
546        // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
547        ct_grlwe_s1s2.mul_grlwe(
548            &module,
549            &mut ct_grlwe_s0s2,
550            &ct_grlwe_s0s1,
551            scratch.borrow(),
552        );
553
554        let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
555        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
556
557        (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
558            ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
559            ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
560            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
561
562            let noise_have: f64 = pt.data.std(0, log_base2k).log2();
563            let noise_want: f64 = noise_grlwe_rlwe_product(
564                module.n() as f64,
565                log_base2k,
566                0.5,
567                0.5,
568                0f64,
569                sigma * sigma,
570                0f64,
571                log_k_grlwe,
572                log_k_grlwe,
573            );
574
575            assert!(
576                (noise_have - noise_want).abs() <= 0.1,
577                "{} {}",
578                noise_have,
579                noise_want
580            );
581        });
582
583        module.free();
584    }
585
586    #[test]
587    fn mul_grlwe_inplace() {
588        let module: Module<FFT64> = Module::<FFT64>::new(2048);
589        let log_base2k: usize = 12;
590        let log_k_grlwe: usize = 60;
591        let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
592
593        let sigma: f64 = 3.2;
594        let bound: f64 = sigma * 6.0;
595
596        let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
597        let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
598
599        let mut source_xs: Source = Source::new([0u8; 32]);
600        let mut source_xe: Source = Source::new([0u8; 32]);
601        let mut source_xa: Source = Source::new([0u8; 32]);
602
603        let mut scratch: ScratchOwned = ScratchOwned::new(
604            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
605                | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s1.size())
606                | GRLWECt::mul_grlwe_scratch_space(
607                    &module,
608                    ct_grlwe_s0s1.size(),
609                    ct_grlwe_s0s1.size(),
610                    ct_grlwe_s1s2.size(),
611                ),
612        );
613
614        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
615        sk0.fill_ternary_prob(0.5, &mut source_xs);
616
617        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
618        sk0_dft.dft(&module, &sk0);
619
620        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
621        sk1.fill_ternary_prob(0.5, &mut source_xs);
622
623        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
624        sk1_dft.dft(&module, &sk1);
625
626        let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
627        sk2.fill_ternary_prob(0.5, &mut source_xs);
628
629        let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
630        sk2_dft.dft(&module, &sk2);
631
632        // GRLWE_{s1}(s0) = s0 -> s1
633        ct_grlwe_s0s1.encrypt_sk(
634            &module,
635            &sk0.data,
636            &sk1_dft,
637            &mut source_xa,
638            &mut source_xe,
639            sigma,
640            bound,
641            scratch.borrow(),
642        );
643
644        // GRLWE_{s2}(s1) -> s1 -> s2
645        ct_grlwe_s1s2.encrypt_sk(
646            &module,
647            &sk1.data,
648            &sk2_dft,
649            &mut source_xa,
650            &mut source_xe,
651            sigma,
652            bound,
653            scratch.borrow(),
654        );
655
656        // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
657        ct_grlwe_s1s2.mul_grlwe_inplace(&module, &mut ct_grlwe_s0s1, scratch.borrow());
658
659        let ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = ct_grlwe_s0s1;
660
661        let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
662        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
663
664        (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
665            ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
666            ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
667            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
668
669            let noise_have: f64 = pt.data.std(0, log_base2k).log2();
670            let noise_want: f64 = noise_grlwe_rlwe_product(
671                module.n() as f64,
672                log_base2k,
673                0.5,
674                0.5,
675                0f64,
676                sigma * sigma,
677                0f64,
678                log_k_grlwe,
679                log_k_grlwe,
680            );
681
682            assert!(
683                (noise_have - noise_want).abs() <= 0.1,
684                "{} {}",
685                noise_have,
686                noise_want
687            );
688        });
689
690        module.free();
691    }
692}
693
694#[allow(dead_code)]
695pub(crate) fn noise_grlwe_rlwe_product(
696    n: f64,
697    log_base2k: usize,
698    var_xs: f64,
699    var_msg: f64,
700    var_a_err: f64,
701    var_gct_err_lhs: f64,
702    var_gct_err_rhs: f64,
703    a_logq: usize,
704    b_logq: usize,
705) -> f64 {
706    let a_logq: usize = a_logq.min(b_logq);
707    let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
708
709    let b_scale = 2.0f64.powi(b_logq as i32);
710    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
711
712    let base: f64 = (1 << (log_base2k)) as f64;
713    let var_base: f64 = base * base / 12f64;
714
715    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
716    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
717    let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
718    noise += var_msg * var_a_err * a_scale * a_scale * n;
719    noise = noise.sqrt();
720    noise /= b_scale;
721    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
722}