arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/glwe_ciphertext.rs
core/src/glwe_ciphertext.rsRaw
  1use backend::{
  2    AddNormal, Backend, FFT64, FillUniform, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxAlloc, ScalarZnxDftAlloc,
  3    ScalarZnxDftOps, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc,
  4    VecZnxDftOps, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxZero,
  5};
  6use sampling::source::Source;
  7
  8use crate::{
  9    AutomorphismKey, GGSWCiphertext, GLWECiphertextFourier, GLWEOps, GLWEPlaintext, GLWEPublicKey, GLWESecret, GLWESwitchingKey,
 10    Infos, SIX_SIGMA, SecretDistribution, SetMetaData,
 11};
 12
 13pub struct GLWECiphertext<C> {
 14    pub data: VecZnx<C>,
 15    pub basek: usize,
 16    pub k: usize,
 17}
 18
 19impl GLWECiphertext<Vec<u8>> {
 20    pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
 21        Self {
 22            data: module.new_vec_znx(rank + 1, k.div_ceil(basek)),
 23            basek,
 24            k,
 25        }
 26    }
 27
 28    pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
 29        module.bytes_of_vec_znx(rank + 1, k.div_ceil(basek))
 30    }
 31}
 32
 33impl<T> Infos for GLWECiphertext<T> {
 34    type Inner = VecZnx<T>;
 35
 36    fn inner(&self) -> &Self::Inner {
 37        &self.data
 38    }
 39
 40    fn basek(&self) -> usize {
 41        self.basek
 42    }
 43
 44    fn k(&self) -> usize {
 45        self.k
 46    }
 47}
 48
 49impl<T> GLWECiphertext<T> {
 50    pub fn rank(&self) -> usize {
 51        self.cols() - 1
 52    }
 53}
 54
 55impl<C: AsRef<[u8]>> GLWECiphertext<C> {
 56    #[allow(dead_code)]
 57    pub(crate) fn dft<R: AsMut<[u8]> + AsRef<[u8]>>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>) {
 58        #[cfg(debug_assertions)]
 59        {
 60            assert_eq!(self.rank(), res.rank());
 61            assert_eq!(self.basek(), res.basek())
 62        }
 63
 64        (0..self.rank() + 1).for_each(|i| {
 65            module.vec_znx_dft(1, 0, &mut res.data, i, &self.data, i);
 66        })
 67    }
 68}
 69
 70impl GLWECiphertext<Vec<u8>> {
 71    pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
 72        let size: usize = k.div_ceil(basek);
 73        module.vec_znx_big_normalize_tmp_bytes() + module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx(1, size)
 74    }
 75    pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
 76        let size: usize = k.div_ceil(basek);
 77        ((module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx_big(1, size)) | module.bytes_of_scalar_znx(1))
 78            + module.bytes_of_scalar_znx_dft(1)
 79            + module.vec_znx_big_normalize_tmp_bytes()
 80    }
 81
 82    pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
 83        let size: usize = k.div_ceil(basek);
 84        (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
 85    }
 86
 87    pub fn keyswitch_scratch_space(
 88        module: &Module<FFT64>,
 89        basek: usize,
 90        k_out: usize,
 91        k_in: usize,
 92        k_ksk: usize,
 93        digits: usize,
 94        rank_in: usize,
 95        rank_out: usize,
 96    ) -> usize {
 97        let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank_out + 1);
 98        let in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
 99        let out_size: usize = k_out.div_ceil(basek);
100        let ksk_size: usize = k_ksk.div_ceil(basek);
101        let ai_dft: usize = module.bytes_of_vec_znx_dft(rank_in, in_size);
102        let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, rank_in, rank_out + 1, ksk_size)
103            + module.bytes_of_vec_znx_dft(rank_in, in_size);
104        let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
105        return res_dft + ((ai_dft + vmp) | normalize);
106    }
107
108    pub fn keyswitch_from_fourier_scratch_space(
109        module: &Module<FFT64>,
110        basek: usize,
111        k_out: usize,
112        k_in: usize,
113        k_ksk: usize,
114        digits: usize,
115        rank_in: usize,
116        rank_out: usize,
117    ) -> usize {
118        Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
119    }
120
121    pub fn keyswitch_inplace_scratch_space(
122        module: &Module<FFT64>,
123        basek: usize,
124        k_out: usize,
125        k_ksk: usize,
126        digits: usize,
127        rank: usize,
128    ) -> usize {
129        Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
130    }
131
132    pub fn automorphism_scratch_space(
133        module: &Module<FFT64>,
134        basek: usize,
135        k_out: usize,
136        k_in: usize,
137        k_ksk: usize,
138        digits: usize,
139        rank: usize,
140    ) -> usize {
141        Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
142    }
143
144    pub fn automorphism_inplace_scratch_space(
145        module: &Module<FFT64>,
146        basek: usize,
147        k_out: usize,
148        k_ksk: usize,
149        digits: usize,
150        rank: usize,
151    ) -> usize {
152        Self::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
153    }
154
155    pub fn external_product_scratch_space(
156        module: &Module<FFT64>,
157        basek: usize,
158        k_out: usize,
159        k_in: usize,
160        ggsw_k: usize,
161        digits: usize,
162        rank: usize,
163    ) -> usize {
164        let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
165        let in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
166        let out_size: usize = k_out.div_ceil(basek);
167        let ggsw_size: usize = ggsw_k.div_ceil(basek);
168        let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
169            + module.vmp_apply_tmp_bytes(
170                out_size,
171                in_size,
172                in_size,  // rows
173                rank + 1, // cols in
174                rank + 1, // cols out
175                ggsw_size,
176            );
177        let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
178        res_dft + (vmp | normalize)
179    }
180
181    pub fn external_product_inplace_scratch_space(
182        module: &Module<FFT64>,
183        basek: usize,
184        k_out: usize,
185        ggsw_k: usize,
186        digits: usize,
187        rank: usize,
188    ) -> usize {
189        Self::external_product_scratch_space(module, basek, k_out, k_out, ggsw_k, digits, rank)
190    }
191}
192
193impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for GLWECiphertext<DataSelf> {
194    fn set_k(&mut self, k: usize) {
195        self.k = k
196    }
197
198    fn set_basek(&mut self, basek: usize) {
199        self.basek = basek
200    }
201}
202
203impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
204    pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
205        &mut self,
206        module: &Module<FFT64>,
207        pt: &GLWEPlaintext<DataPt>,
208        sk: &GLWESecret<DataSk, FFT64>,
209        source_xa: &mut Source,
210        source_xe: &mut Source,
211        sigma: f64,
212        scratch: &mut Scratch,
213    ) {
214        self.encrypt_sk_private(
215            module,
216            Some((pt, 0)),
217            sk,
218            source_xa,
219            source_xe,
220            sigma,
221            scratch,
222        );
223    }
224
225    pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
226        &mut self,
227        module: &Module<FFT64>,
228        sk: &GLWESecret<DataSk, FFT64>,
229        source_xa: &mut Source,
230        source_xe: &mut Source,
231        sigma: f64,
232        scratch: &mut Scratch,
233    ) {
234        self.encrypt_sk_private(
235            module,
236            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
237            sk,
238            source_xa,
239            source_xe,
240            sigma,
241            scratch,
242        );
243    }
244
245    pub fn encrypt_pk<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
246        &mut self,
247        module: &Module<FFT64>,
248        pt: &GLWEPlaintext<DataPt>,
249        pk: &GLWEPublicKey<DataPk, FFT64>,
250        source_xu: &mut Source,
251        source_xe: &mut Source,
252        sigma: f64,
253        scratch: &mut Scratch,
254    ) {
255        self.encrypt_pk_private(
256            module,
257            Some((pt, 0)),
258            pk,
259            source_xu,
260            source_xe,
261            sigma,
262            scratch,
263        );
264    }
265
266    pub fn encrypt_zero_pk<DataPk: AsRef<[u8]>>(
267        &mut self,
268        module: &Module<FFT64>,
269        pk: &GLWEPublicKey<DataPk, FFT64>,
270        source_xu: &mut Source,
271        source_xe: &mut Source,
272        sigma: f64,
273        scratch: &mut Scratch,
274    ) {
275        self.encrypt_pk_private(
276            module,
277            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
278            pk,
279            source_xu,
280            source_xe,
281            sigma,
282            scratch,
283        );
284    }
285
286    pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
287        &mut self,
288        module: &Module<FFT64>,
289        lhs: &GLWECiphertext<DataLhs>,
290        rhs: &AutomorphismKey<DataRhs, FFT64>,
291        scratch: &mut Scratch,
292    ) {
293        self.keyswitch(module, lhs, &rhs.key, scratch);
294        (0..self.rank() + 1).for_each(|i| {
295            module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
296        })
297    }
298
299    pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
300        &mut self,
301        module: &Module<FFT64>,
302        rhs: &AutomorphismKey<DataRhs, FFT64>,
303        scratch: &mut Scratch,
304    ) {
305        self.keyswitch_inplace(module, &rhs.key, scratch);
306        (0..self.rank() + 1).for_each(|i| {
307            module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
308        })
309    }
310
311    pub fn automorphism_add<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
312        &mut self,
313        module: &Module<FFT64>,
314        lhs: &GLWECiphertext<DataLhs>,
315        rhs: &AutomorphismKey<DataRhs, FFT64>,
316        scratch: &mut Scratch,
317    ) {
318        Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);
319    }
320
321    pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>(
322        &mut self,
323        module: &Module<FFT64>,
324        rhs: &AutomorphismKey<DataRhs, FFT64>,
325        scratch: &mut Scratch,
326    ) {
327        unsafe {
328            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
329            Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
330        }
331    }
332
333    pub fn automorphism_sub_ab<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
334        &mut self,
335        module: &Module<FFT64>,
336        lhs: &GLWECiphertext<DataLhs>,
337        rhs: &AutomorphismKey<DataRhs, FFT64>,
338        scratch: &mut Scratch,
339    ) {
340        Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);
341    }
342
343    pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>(
344        &mut self,
345        module: &Module<FFT64>,
346        rhs: &AutomorphismKey<DataRhs, FFT64>,
347        scratch: &mut Scratch,
348    ) {
349        unsafe {
350            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
351            Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
352        }
353    }
354
355    pub fn automorphism_sub_ba<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
356        &mut self,
357        module: &Module<FFT64>,
358        lhs: &GLWECiphertext<DataLhs>,
359        rhs: &AutomorphismKey<DataRhs, FFT64>,
360        scratch: &mut Scratch,
361    ) {
362        Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);
363    }
364
365    pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>(
366        &mut self,
367        module: &Module<FFT64>,
368        rhs: &AutomorphismKey<DataRhs, FFT64>,
369        scratch: &mut Scratch,
370    ) {
371        unsafe {
372            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
373            Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
374        }
375    }
376
377    pub(crate) fn keyswitch_from_fourier<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
378        &mut self,
379        module: &Module<FFT64>,
380        lhs: &GLWECiphertextFourier<DataLhs, FFT64>,
381        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
382        scratch: &mut Scratch,
383    ) {
384        let basek: usize = self.basek();
385
386        #[cfg(debug_assertions)]
387        {
388            assert_eq!(lhs.rank(), rhs.rank_in());
389            assert_eq!(self.rank(), rhs.rank_out());
390            assert_eq!(self.basek(), basek);
391            assert_eq!(lhs.basek(), basek);
392            assert_eq!(rhs.n(), module.n());
393            assert_eq!(self.n(), module.n());
394            assert_eq!(lhs.n(), module.n());
395            assert!(
396                scratch.available()
397                    >= GLWECiphertext::keyswitch_from_fourier_scratch_space(
398                        module,
399                        self.basek(),
400                        self.k(),
401                        lhs.k(),
402                        rhs.k(),
403                        rhs.digits(),
404                        rhs.rank_in(),
405                        rhs.rank_out(),
406                    )
407            );
408        }
409
410        let cols_in: usize = rhs.rank_in();
411        let cols_out: usize = rhs.rank_out() + 1;
412
413        // Buffer of the result of VMP in DFT
414        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
415
416        {
417            let digits = rhs.digits();
418
419            (0..digits).for_each(|di| {
420                // (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
421                let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + di) / digits);
422
423                (0..cols_in).for_each(|col_i| {
424                    module.vec_znx_dft_copy(
425                        digits,
426                        digits - 1 - di,
427                        &mut ai_dft,
428                        col_i,
429                        &lhs.data,
430                        col_i + 1,
431                    );
432                });
433
434                if di == 0 {
435                    module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
436                } else {
437                    module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
438                }
439            });
440        }
441
442        module.vec_znx_dft_add_inplace(&mut res_dft, 0, &lhs.data, 0);
443
444        // Switches result of VMP outside of DFT
445        let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
446
447        (0..cols_out).for_each(|i| {
448            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
449        });
450    }
451
452    pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
453        &mut self,
454        module: &Module<FFT64>,
455        lhs: &GLWECiphertext<DataLhs>,
456        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
457        scratch: &mut Scratch,
458    ) {
459        Self::keyswitch_private::<_, _, 0>(self, 0, module, lhs, rhs, scratch);
460    }
461
462    pub(crate) fn keyswitch_private<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>, const OP: u8>(
463        &mut self,
464        apply_auto: i64,
465        module: &Module<FFT64>,
466        lhs: &GLWECiphertext<DataLhs>,
467        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
468        scratch: &mut Scratch,
469    ) {
470        let basek: usize = self.basek();
471
472        #[cfg(debug_assertions)]
473        {
474            assert_eq!(lhs.rank(), rhs.rank_in());
475            assert_eq!(self.rank(), rhs.rank_out());
476            assert_eq!(self.basek(), basek);
477            assert_eq!(lhs.basek(), basek);
478            assert_eq!(rhs.n(), module.n());
479            assert_eq!(self.n(), module.n());
480            assert_eq!(lhs.n(), module.n());
481            assert!(
482                scratch.available()
483                    >= GLWECiphertext::keyswitch_scratch_space(
484                        module,
485                        self.basek(),
486                        self.k(),
487                        lhs.k(),
488                        rhs.k(),
489                        rhs.digits(),
490                        rhs.rank_in(),
491                        rhs.rank_out(),
492                    )
493            );
494        }
495
496        let cols_in: usize = rhs.rank_in();
497        let cols_out: usize = rhs.rank_out() + 1;
498        let digits: usize = rhs.digits();
499
500        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
501        let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + digits - 1) / digits);
502        ai_dft.zero();
503        {
504            (0..digits).for_each(|di| {
505                ai_dft.set_size((lhs.size() + di) / digits);
506
507                // Small optimization for digits > 2
508                // VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
509                // we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
510                // As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
511                // It is possible to further ignore the last digits-1 limbs, but this introduce
512                // ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
513                // noise is kept with respect to the ideal functionality.
514                res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
515
516                (0..cols_in).for_each(|col_i| {
517                    module.vec_znx_dft(
518                        digits,
519                        digits - di - 1,
520                        &mut ai_dft,
521                        col_i,
522                        &lhs.data,
523                        col_i + 1,
524                    );
525                });
526
527                if di == 0 {
528                    module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
529                } else {
530                    module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
531                }
532            });
533        }
534
535        let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
536
537        module.vec_znx_big_add_small_inplace(&mut res_big, 0, &lhs.data, 0);
538
539        (0..cols_out).for_each(|i| {
540            if apply_auto != 0 {
541                module.vec_znx_big_automorphism_inplace(apply_auto, &mut res_big, i);
542            }
543
544            match OP {
545                1 => module.vec_znx_big_add_small_inplace(&mut res_big, i, &lhs.data, i),
546                2 => module.vec_znx_big_sub_small_a_inplace(&mut res_big, i, &lhs.data, i),
547                3 => module.vec_znx_big_sub_small_b_inplace(&mut res_big, i, &lhs.data, i),
548                _ => {}
549            }
550            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
551        });
552    }
553
554    pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
555        &mut self,
556        module: &Module<FFT64>,
557        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
558        scratch: &mut Scratch,
559    ) {
560        unsafe {
561            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
562            self.keyswitch(&module, &*self_ptr, rhs, scratch);
563        }
564    }
565
566    pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
567        &mut self,
568        module: &Module<FFT64>,
569        lhs: &GLWECiphertext<DataLhs>,
570        rhs: &GGSWCiphertext<DataRhs, FFT64>,
571        scratch: &mut Scratch,
572    ) {
573        let basek: usize = self.basek();
574
575        #[cfg(debug_assertions)]
576        {
577            assert_eq!(rhs.rank(), lhs.rank());
578            assert_eq!(rhs.rank(), self.rank());
579            assert_eq!(self.basek(), basek);
580            assert_eq!(lhs.basek(), basek);
581            assert_eq!(rhs.n(), module.n());
582            assert_eq!(self.n(), module.n());
583            assert_eq!(lhs.n(), module.n());
584            assert!(
585                scratch.available()
586                    >= GLWECiphertext::external_product_scratch_space(
587                        module,
588                        self.basek(),
589                        self.k(),
590                        lhs.k(),
591                        rhs.k(),
592                        rhs.digits(),
593                        rhs.rank(),
594                    )
595            );
596        }
597
598        let cols: usize = rhs.rank() + 1;
599        let digits: usize = rhs.digits();
600
601        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
602        let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + digits - 1) / digits);
603
604        {
605            (0..digits).for_each(|di| {
606                // (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
607                a_dft.set_size((lhs.size() + di) / digits);
608
609                // Small optimization for digits > 2
610                // VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
611                // we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
612                // As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
613                // It is possible to further ignore the last digits-1 limbs, but this introduce
614                // ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
615                // noise is kept with respect to the ideal functionality.
616                res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
617
618                (0..cols).for_each(|col_i| {
619                    module.vec_znx_dft(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
620                });
621
622                if di == 0 {
623                    module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
624                } else {
625                    module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
626                }
627            });
628        }
629
630        let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
631
632        (0..cols).for_each(|i| {
633            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
634        });
635    }
636
637    pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
638        &mut self,
639        module: &Module<FFT64>,
640        rhs: &GGSWCiphertext<DataRhs, FFT64>,
641        scratch: &mut Scratch,
642    ) {
643        unsafe {
644            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
645            self.external_product(&module, &*self_ptr, rhs, scratch);
646        }
647    }
648
649    pub(crate) fn encrypt_sk_private<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
650        &mut self,
651        module: &Module<FFT64>,
652        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
653        sk: &GLWESecret<DataSk, FFT64>,
654        source_xa: &mut Source,
655        source_xe: &mut Source,
656        sigma: f64,
657        scratch: &mut Scratch,
658    ) {
659        #[cfg(debug_assertions)]
660        {
661            assert_eq!(self.rank(), sk.rank());
662            assert_eq!(sk.n(), module.n());
663            assert_eq!(self.n(), module.n());
664            if let Some((pt, col)) = pt {
665                assert_eq!(pt.n(), module.n());
666                assert!(col < self.rank() + 1);
667            }
668            assert!(
669                scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
670                "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
671                scratch.available(),
672                GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
673            )
674        }
675
676        let basek: usize = self.basek();
677        let k: usize = self.k();
678        let size: usize = self.size();
679        let cols: usize = self.rank() + 1;
680
681        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
682        c0_big.zero();
683
684        {
685            // c[i] = uniform
686            // c[0] -= c[i] * s[i],
687            (1..cols).for_each(|i| {
688                let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
689
690                // c[i] = uniform
691                self.data.fill_uniform(basek, i, size, source_xa);
692
693                // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
694                module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
695                module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
696                let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
697
698                // use c[0] as buffer, which is overwritten later by the normalization step
699                module.vec_znx_big_normalize(basek, &mut self.data, 0, &ci_big, 0, scratch_2);
700
701                // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
702                module.vec_znx_sub_ab_inplace(&mut c0_big, 0, &self.data, 0);
703
704                // c[i] += m if col = i
705                if let Some((pt, col)) = pt {
706                    if i == col {
707                        module.vec_znx_add_inplace(&mut self.data, i, &pt.data, 0);
708                        module.vec_znx_normalize_inplace(basek, &mut self.data, i, scratch_2);
709                    }
710                }
711            });
712        }
713
714        // c[0] += e
715        c0_big.add_normal(basek, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
716
717        // c[0] += m if col = 0
718        if let Some((pt, col)) = pt {
719            if col == 0 {
720                module.vec_znx_add_inplace(&mut c0_big, 0, &pt.data, 0);
721            }
722        }
723
724        // c[0] = norm(c[0])
725        module.vec_znx_normalize(basek, &mut self.data, 0, &c0_big, 0, scratch_1);
726    }
727
728    pub(crate) fn encrypt_pk_private<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
729        &mut self,
730        module: &Module<FFT64>,
731        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
732        pk: &GLWEPublicKey<DataPk, FFT64>,
733        source_xu: &mut Source,
734        source_xe: &mut Source,
735        sigma: f64,
736        scratch: &mut Scratch,
737    ) {
738        #[cfg(debug_assertions)]
739        {
740            assert_eq!(self.basek(), pk.basek());
741            assert_eq!(self.n(), module.n());
742            assert_eq!(pk.n(), module.n());
743            assert_eq!(self.rank(), pk.rank());
744            if let Some((pt, _)) = pt {
745                assert_eq!(pt.basek(), pk.basek());
746                assert_eq!(pt.n(), module.n());
747            }
748        }
749
750        let basek: usize = pk.basek();
751        let size_pk: usize = pk.size();
752        let cols: usize = self.rank() + 1;
753
754        // Generates u according to the underlying secret distribution.
755        let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
756
757        {
758            let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
759            match pk.dist {
760                SecretDistribution::NONE => panic!(
761                    "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
762                     Self::generate"
763                ),
764                SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
765                SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
766                SecretDistribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu),
767                SecretDistribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu),
768                SecretDistribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu),
769                SecretDistribution::ZERO => {}
770            }
771
772            module.svp_prepare(&mut u_dft, 0, &u, 0);
773        }
774
775        // ct[i] = pk[i] * u + ei (+ m if col = i)
776        (0..cols).for_each(|i| {
777            let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
778            // ci_dft = DFT(u) * DFT(pk[i])
779            module.svp_apply(&mut ci_dft, 0, &u_dft, 0, &pk.data.data, i);
780
781            // ci_big = u * p[i]
782            let mut ci_big = module.vec_znx_idft_consume(ci_dft);
783
784            // ci_big = u * pk[i] + e
785            ci_big.add_normal(basek, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
786
787            // ci_big = u * pk[i] + e + m (if col = i)
788            if let Some((pt, col)) = pt {
789                if col == i {
790                    module.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.data, 0);
791                }
792            }
793
794            // ct[i] = norm(ci_big)
795            module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
796        });
797    }
798}
799
800impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
801    pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
802        GLWECiphertext {
803            data: self.data.clone(),
804            basek: self.basek(),
805            k: self.k(),
806        }
807    }
808
809    pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
810        &self,
811        module: &Module<FFT64>,
812        pt: &mut GLWEPlaintext<DataPt>,
813        sk: &GLWESecret<DataSk, FFT64>,
814        scratch: &mut Scratch,
815    ) {
816        #[cfg(debug_assertions)]
817        {
818            assert_eq!(self.rank(), sk.rank());
819            assert_eq!(self.n(), module.n());
820            assert_eq!(pt.n(), module.n());
821            assert_eq!(sk.n(), module.n());
822        }
823
824        let cols: usize = self.rank() + 1;
825
826        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
827        c0_big.zero();
828
829        {
830            (1..cols).for_each(|i| {
831                // ci_dft = DFT(a[i]) * DFT(s[i])
832                let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
833                module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
834                module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
835                let ci_big = module.vec_znx_idft_consume(ci_dft);
836
837                // c0_big += a[i] * s[i]
838                module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
839            });
840        }
841
842        // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
843        module.vec_znx_big_add_small_inplace(&mut c0_big, 0, &self.data, 0);
844
845        // pt = norm(BIG(m + e))
846        module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut c0_big, 0, scratch_1);
847
848        pt.basek = self.basek();
849        pt.k = pt.k().min(self.k());
850    }
851}
852
853pub trait GLWECiphertextToRef {
854    fn to_ref(&self) -> GLWECiphertext<&[u8]>;
855}
856
857impl<D: AsRef<[u8]>> GLWECiphertextToRef for GLWECiphertext<D> {
858    fn to_ref(&self) -> GLWECiphertext<&[u8]> {
859        GLWECiphertext {
860            data: self.data.to_ref(),
861            basek: self.basek,
862            k: self.k,
863        }
864    }
865}
866
867pub trait GLWECiphertextToMut {
868    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
869}
870
871impl<D: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextToMut for GLWECiphertext<D> {
872    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
873        GLWECiphertext {
874            data: self.data.to_mut(),
875            basek: self.basek,
876            k: self.k,
877        }
878    }
879}
880
881impl<D> GLWEOps for GLWECiphertext<D>
882where
883    D: AsRef<[u8]> + AsMut<[u8]>,
884    GLWECiphertext<D>: GLWECiphertextToMut + Infos + SetMetaData,
885{
886}