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, derive_size,
 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, derive_size(basek, k)),
 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, derive_size(basek, k))
 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(&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 = derive_size(basek, k);
 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 = derive_size(basek, k);
 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 = derive_size(basek, k);
 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        out_k: usize,
 91        out_rank: usize,
 92        in_k: usize,
 93        in_rank: usize,
 94        ksk_k: usize,
 95    ) -> usize {
 96        let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, out_rank);
 97        let in_size: usize = derive_size(basek, in_k);
 98        let out_size: usize = derive_size(basek, out_k);
 99        let ksk_size: usize = derive_size(basek, ksk_k);
100        let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, in_rank, out_rank + 1, ksk_size)
101            + module.bytes_of_vec_znx_dft(in_rank, in_size);
102        let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
103        return res_dft + (vmp | normalize);
104    }
105
106    pub fn keyswitch_from_fourier_scratch_space(
107        module: &Module<FFT64>,
108        basek: usize,
109        out_k: usize,
110        out_rank: usize,
111        in_k: usize,
112        in_rank: usize,
113        ksk_k: usize,
114    ) -> usize {
115        Self::keyswitch_scratch_space(module, basek, out_k, out_rank, in_k, in_rank, ksk_k)
116    }
117
118    pub fn keyswitch_inplace_scratch_space(
119        module: &Module<FFT64>,
120        basek: usize,
121        out_k: usize,
122        out_rank: usize,
123        ksk_k: usize,
124    ) -> usize {
125        Self::keyswitch_scratch_space(module, basek, out_k, out_rank, out_k, out_rank, ksk_k)
126    }
127
128    pub fn automorphism_scratch_space(
129        module: &Module<FFT64>,
130        basek: usize,
131        out_k: usize,
132        in_k: usize,
133        atk_k: usize,
134        rank: usize,
135    ) -> usize {
136        Self::keyswitch_scratch_space(module, basek, out_k, rank, in_k, rank, atk_k)
137    }
138
139    pub fn automorphism_inplace_scratch_space(
140        module: &Module<FFT64>,
141        basek: usize,
142        out_k: usize,
143        atk_k: usize,
144        rank: usize,
145    ) -> usize {
146        Self::keyswitch_scratch_space(module, basek, out_k, rank, out_k, rank, atk_k)
147    }
148
149    pub fn external_product_scratch_space(
150        module: &Module<FFT64>,
151        basek: usize,
152        out_k: usize,
153        in_k: usize,
154        ggsw_k: usize,
155        rank: usize,
156    ) -> usize {
157        let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
158        let in_size: usize = derive_size(basek, in_k);
159        let out_size: usize = derive_size(basek, out_k);
160        let ggsw_size: usize = derive_size(basek, ggsw_k);
161        let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
162            + module.vmp_apply_tmp_bytes(
163                out_size,
164                in_size,
165                in_size,  // rows
166                rank + 1, // cols in
167                rank + 1, // cols out
168                ggsw_size,
169            );
170        let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
171        res_dft + (vmp | normalize)
172    }
173
174    pub fn external_product_inplace_scratch_space(
175        module: &Module<FFT64>,
176        basek: usize,
177        out_k: usize,
178        ggsw_k: usize,
179        rank: usize,
180    ) -> usize {
181        Self::external_product_scratch_space(module, basek, out_k, out_k, ggsw_k, rank)
182    }
183}
184
185impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for GLWECiphertext<DataSelf> {
186    fn set_k(&mut self, k: usize) {
187        self.k = k
188    }
189
190    fn set_basek(&mut self, basek: usize) {
191        self.basek = basek
192    }
193}
194
195impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
196    pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
197        &mut self,
198        module: &Module<FFT64>,
199        pt: &GLWEPlaintext<DataPt>,
200        sk: &GLWESecret<DataSk, FFT64>,
201        source_xa: &mut Source,
202        source_xe: &mut Source,
203        sigma: f64,
204        scratch: &mut Scratch,
205    ) {
206        self.encrypt_sk_private(
207            module,
208            Some((pt, 0)),
209            sk,
210            source_xa,
211            source_xe,
212            sigma,
213            scratch,
214        );
215    }
216
217    pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
218        &mut self,
219        module: &Module<FFT64>,
220        sk: &GLWESecret<DataSk, FFT64>,
221        source_xa: &mut Source,
222        source_xe: &mut Source,
223        sigma: f64,
224        scratch: &mut Scratch,
225    ) {
226        self.encrypt_sk_private(
227            module,
228            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
229            sk,
230            source_xa,
231            source_xe,
232            sigma,
233            scratch,
234        );
235    }
236
237    pub fn encrypt_pk<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
238        &mut self,
239        module: &Module<FFT64>,
240        pt: &GLWEPlaintext<DataPt>,
241        pk: &GLWEPublicKey<DataPk, FFT64>,
242        source_xu: &mut Source,
243        source_xe: &mut Source,
244        sigma: f64,
245        scratch: &mut Scratch,
246    ) {
247        self.encrypt_pk_private(
248            module,
249            Some((pt, 0)),
250            pk,
251            source_xu,
252            source_xe,
253            sigma,
254            scratch,
255        );
256    }
257
258    pub fn encrypt_zero_pk<DataPk: AsRef<[u8]>>(
259        &mut self,
260        module: &Module<FFT64>,
261        pk: &GLWEPublicKey<DataPk, FFT64>,
262        source_xu: &mut Source,
263        source_xe: &mut Source,
264        sigma: f64,
265        scratch: &mut Scratch,
266    ) {
267        self.encrypt_pk_private(
268            module,
269            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
270            pk,
271            source_xu,
272            source_xe,
273            sigma,
274            scratch,
275        );
276    }
277
278    pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
279        &mut self,
280        module: &Module<FFT64>,
281        lhs: &GLWECiphertext<DataLhs>,
282        rhs: &AutomorphismKey<DataRhs, FFT64>,
283        scratch: &mut Scratch,
284    ) {
285        self.keyswitch(module, lhs, &rhs.key, scratch);
286        (0..self.rank() + 1).for_each(|i| {
287            module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
288        })
289    }
290
291    pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
292        &mut self,
293        module: &Module<FFT64>,
294        rhs: &AutomorphismKey<DataRhs, FFT64>,
295        scratch: &mut Scratch,
296    ) {
297        self.keyswitch_inplace(module, &rhs.key, scratch);
298        (0..self.rank() + 1).for_each(|i| {
299            module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
300        })
301    }
302
303    pub fn automorphism_add<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
304        &mut self,
305        module: &Module<FFT64>,
306        lhs: &GLWECiphertext<DataLhs>,
307        rhs: &AutomorphismKey<DataRhs, FFT64>,
308        scratch: &mut Scratch,
309    ) {
310        Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);
311    }
312
313    pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>(
314        &mut self,
315        module: &Module<FFT64>,
316        rhs: &AutomorphismKey<DataRhs, FFT64>,
317        scratch: &mut Scratch,
318    ) {
319        unsafe {
320            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
321            Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
322        }
323    }
324
325    pub fn automorphism_sub_ab<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
326        &mut self,
327        module: &Module<FFT64>,
328        lhs: &GLWECiphertext<DataLhs>,
329        rhs: &AutomorphismKey<DataRhs, FFT64>,
330        scratch: &mut Scratch,
331    ) {
332        Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);
333    }
334
335    pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>(
336        &mut self,
337        module: &Module<FFT64>,
338        rhs: &AutomorphismKey<DataRhs, FFT64>,
339        scratch: &mut Scratch,
340    ) {
341        unsafe {
342            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
343            Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
344        }
345    }
346
347    pub fn automorphism_sub_ba<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
348        &mut self,
349        module: &Module<FFT64>,
350        lhs: &GLWECiphertext<DataLhs>,
351        rhs: &AutomorphismKey<DataRhs, FFT64>,
352        scratch: &mut Scratch,
353    ) {
354        Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);
355    }
356
357    pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>(
358        &mut self,
359        module: &Module<FFT64>,
360        rhs: &AutomorphismKey<DataRhs, FFT64>,
361        scratch: &mut Scratch,
362    ) {
363        unsafe {
364            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
365            Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
366        }
367    }
368
369    pub(crate) fn keyswitch_from_fourier<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
370        &mut self,
371        module: &Module<FFT64>,
372        lhs: &GLWECiphertextFourier<DataLhs, FFT64>,
373        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
374        scratch: &mut Scratch,
375    ) {
376        let basek: usize = self.basek();
377
378        #[cfg(debug_assertions)]
379        {
380            assert_eq!(lhs.rank(), rhs.rank_in());
381            assert_eq!(self.rank(), rhs.rank_out());
382            assert_eq!(self.basek(), basek);
383            assert_eq!(lhs.basek(), basek);
384            assert_eq!(rhs.n(), module.n());
385            assert_eq!(self.n(), module.n());
386            assert_eq!(lhs.n(), module.n());
387            assert!(
388                scratch.available()
389                    >= GLWECiphertext::keyswitch_from_fourier_scratch_space(
390                        module,
391                        self.basek(),
392                        self.k(),
393                        self.rank(),
394                        lhs.k(),
395                        lhs.rank(),
396                        rhs.k(),
397                    )
398            );
399        }
400
401        let cols_in: usize = rhs.rank_in();
402        let cols_out: usize = rhs.rank_out() + 1;
403
404        // Buffer of the result of VMP in DFT
405        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
406
407        {
408            // Applies VMP
409            let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
410            (0..cols_in).for_each(|col_i| {
411                module.vec_znx_dft_copy(&mut ai_dft, col_i, &lhs.data, col_i + 1);
412            });
413            module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
414        }
415
416        module.vec_znx_dft_add_inplace(&mut res_dft, 0, &lhs.data, 0);
417
418        // Switches result of VMP outside of DFT
419        let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
420
421        (0..cols_out).for_each(|i| {
422            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
423        });
424    }
425
426    pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
427        &mut self,
428        module: &Module<FFT64>,
429        lhs: &GLWECiphertext<DataLhs>,
430        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
431        scratch: &mut Scratch,
432    ) {
433        Self::keyswitch_private::<_, _, 0>(self, 0, module, lhs, rhs, scratch);
434    }
435
436    pub(crate) fn keyswitch_private<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>, const OP: u8>(
437        &mut self,
438        apply_auto: i64,
439        module: &Module<FFT64>,
440        lhs: &GLWECiphertext<DataLhs>,
441        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
442        scratch: &mut Scratch,
443    ) {
444        let basek: usize = self.basek();
445
446        #[cfg(debug_assertions)]
447        {
448            assert_eq!(lhs.rank(), rhs.rank_in());
449            assert_eq!(self.rank(), rhs.rank_out());
450            assert_eq!(self.basek(), basek);
451            assert_eq!(lhs.basek(), basek);
452            assert_eq!(rhs.n(), module.n());
453            assert_eq!(self.n(), module.n());
454            assert_eq!(lhs.n(), module.n());
455            assert!(
456                scratch.available()
457                    >= GLWECiphertext::keyswitch_scratch_space(
458                        module,
459                        self.basek(),
460                        self.k(),
461                        self.rank(),
462                        lhs.k(),
463                        lhs.rank(),
464                        rhs.k(),
465                    )
466            );
467        }
468
469        let cols_in: usize = rhs.rank_in();
470        let cols_out: usize = rhs.rank_out() + 1;
471
472        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
473
474        {
475            let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
476            (0..cols_in).for_each(|col_i| {
477                module.vec_znx_dft(&mut ai_dft, col_i, &lhs.data, col_i + 1);
478            });
479            module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
480        }
481
482        let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
483
484        module.vec_znx_big_add_small_inplace(&mut res_big, 0, &lhs.data, 0);
485
486        (0..cols_out).for_each(|i| {
487            if apply_auto != 0 {
488                module.vec_znx_big_automorphism_inplace(apply_auto, &mut res_big, i);
489            }
490
491            match OP {
492                1 => module.vec_znx_big_add_small_inplace(&mut res_big, i, &lhs.data, i),
493                2 => module.vec_znx_big_sub_small_a_inplace(&mut res_big, i, &lhs.data, i),
494                3 => module.vec_znx_big_sub_small_b_inplace(&mut res_big, i, &lhs.data, i),
495                _ => {}
496            }
497            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
498        });
499    }
500
501    pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
502        &mut self,
503        module: &Module<FFT64>,
504        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
505        scratch: &mut Scratch,
506    ) {
507        unsafe {
508            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
509            self.keyswitch(&module, &*self_ptr, rhs, scratch);
510        }
511    }
512
513    pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
514        &mut self,
515        module: &Module<FFT64>,
516        lhs: &GLWECiphertext<DataLhs>,
517        rhs: &GGSWCiphertext<DataRhs, FFT64>,
518        scratch: &mut Scratch,
519    ) {
520        let basek: usize = self.basek();
521
522        #[cfg(debug_assertions)]
523        {
524            assert_eq!(rhs.rank(), lhs.rank());
525            assert_eq!(rhs.rank(), self.rank());
526            assert_eq!(self.basek(), basek);
527            assert_eq!(lhs.basek(), basek);
528            assert_eq!(rhs.n(), module.n());
529            assert_eq!(self.n(), module.n());
530            assert_eq!(lhs.n(), module.n());
531        }
532
533        let cols: usize = rhs.rank() + 1;
534
535        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
536
537        {
538            let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
539            (0..cols).for_each(|col_i| {
540                module.vec_znx_dft(&mut a_dft, col_i, &lhs.data, col_i);
541            });
542            module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
543        }
544
545        let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
546
547        (0..cols).for_each(|i| {
548            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
549        });
550    }
551
552    pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
553        &mut self,
554        module: &Module<FFT64>,
555        rhs: &GGSWCiphertext<DataRhs, FFT64>,
556        scratch: &mut Scratch,
557    ) {
558        unsafe {
559            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
560            self.external_product(&module, &*self_ptr, rhs, scratch);
561        }
562    }
563
564    pub(crate) fn encrypt_sk_private<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
565        &mut self,
566        module: &Module<FFT64>,
567        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
568        sk: &GLWESecret<DataSk, FFT64>,
569        source_xa: &mut Source,
570        source_xe: &mut Source,
571        sigma: f64,
572        scratch: &mut Scratch,
573    ) {
574        #[cfg(debug_assertions)]
575        {
576            assert_eq!(self.rank(), sk.rank());
577            assert_eq!(sk.n(), module.n());
578            assert_eq!(self.n(), module.n());
579            if let Some((pt, col)) = pt {
580                assert_eq!(pt.n(), module.n());
581                assert!(col < self.rank() + 1);
582            }
583            assert!(
584                scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
585                "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
586                scratch.available(),
587                GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
588            )
589        }
590
591        let basek: usize = self.basek();
592        let k: usize = self.k();
593        let size: usize = self.size();
594        let cols: usize = self.rank() + 1;
595
596        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
597        c0_big.zero();
598
599        {
600            // c[i] = uniform
601            // c[0] -= c[i] * s[i],
602            (1..cols).for_each(|i| {
603                let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
604
605                // c[i] = uniform
606                self.data.fill_uniform(basek, i, size, source_xa);
607
608                // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
609                module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
610                module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
611                let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
612
613                // use c[0] as buffer, which is overwritten later by the normalization step
614                module.vec_znx_big_normalize(basek, &mut self.data, 0, &ci_big, 0, scratch_2);
615
616                // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
617                module.vec_znx_sub_ab_inplace(&mut c0_big, 0, &self.data, 0);
618
619                // c[i] += m if col = i
620                if let Some((pt, col)) = pt {
621                    if i == col {
622                        module.vec_znx_add_inplace(&mut self.data, i, &pt.data, 0);
623                        module.vec_znx_normalize_inplace(basek, &mut self.data, i, scratch_2);
624                    }
625                }
626            });
627        }
628
629        // c[0] += e
630        c0_big.add_normal(basek, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
631
632        // c[0] += m if col = 0
633        if let Some((pt, col)) = pt {
634            if col == 0 {
635                module.vec_znx_add_inplace(&mut c0_big, 0, &pt.data, 0);
636            }
637        }
638
639        // c[0] = norm(c[0])
640        module.vec_znx_normalize(basek, &mut self.data, 0, &c0_big, 0, scratch_1);
641    }
642
643    pub(crate) fn encrypt_pk_private<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
644        &mut self,
645        module: &Module<FFT64>,
646        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
647        pk: &GLWEPublicKey<DataPk, FFT64>,
648        source_xu: &mut Source,
649        source_xe: &mut Source,
650        sigma: f64,
651        scratch: &mut Scratch,
652    ) {
653        #[cfg(debug_assertions)]
654        {
655            assert_eq!(self.basek(), pk.basek());
656            assert_eq!(self.n(), module.n());
657            assert_eq!(pk.n(), module.n());
658            assert_eq!(self.rank(), pk.rank());
659            if let Some((pt, _)) = pt {
660                assert_eq!(pt.basek(), pk.basek());
661                assert_eq!(pt.n(), module.n());
662            }
663        }
664
665        let basek: usize = pk.basek();
666        let size_pk: usize = pk.size();
667        let cols: usize = self.rank() + 1;
668
669        // Generates u according to the underlying secret distribution.
670        let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
671
672        {
673            let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
674            match pk.dist {
675                SecretDistribution::NONE => panic!(
676                    "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
677                     Self::generate"
678                ),
679                SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
680                SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
681                SecretDistribution::ZERO => {}
682            }
683
684            module.svp_prepare(&mut u_dft, 0, &u, 0);
685        }
686
687        // ct[i] = pk[i] * u + ei (+ m if col = i)
688        (0..cols).for_each(|i| {
689            let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
690            // ci_dft = DFT(u) * DFT(pk[i])
691            module.svp_apply(&mut ci_dft, 0, &u_dft, 0, &pk.data.data, i);
692
693            // ci_big = u * p[i]
694            let mut ci_big = module.vec_znx_idft_consume(ci_dft);
695
696            // ci_big = u * pk[i] + e
697            ci_big.add_normal(basek, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
698
699            // ci_big = u * pk[i] + e + m (if col = i)
700            if let Some((pt, col)) = pt {
701                if col == i {
702                    module.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.data, 0);
703                }
704            }
705
706            // ct[i] = norm(ci_big)
707            module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
708        });
709    }
710}
711
712impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
713    pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
714        GLWECiphertext {
715            data: self.data.clone(),
716            basek: self.basek(),
717            k: self.k(),
718        }
719    }
720
721    pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
722        &self,
723        module: &Module<FFT64>,
724        pt: &mut GLWEPlaintext<DataPt>,
725        sk: &GLWESecret<DataSk, FFT64>,
726        scratch: &mut Scratch,
727    ) {
728        #[cfg(debug_assertions)]
729        {
730            assert_eq!(self.rank(), sk.rank());
731            assert_eq!(self.n(), module.n());
732            assert_eq!(pt.n(), module.n());
733            assert_eq!(sk.n(), module.n());
734        }
735
736        let cols: usize = self.rank() + 1;
737
738        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
739        c0_big.zero();
740
741        {
742            (1..cols).for_each(|i| {
743                // ci_dft = DFT(a[i]) * DFT(s[i])
744                let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
745                module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
746                module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
747                let ci_big = module.vec_znx_idft_consume(ci_dft);
748
749                // c0_big += a[i] * s[i]
750                module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
751            });
752        }
753
754        // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
755        module.vec_znx_big_add_small_inplace(&mut c0_big, 0, &self.data, 0);
756
757        // pt = norm(BIG(m + e))
758        module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut c0_big, 0, scratch_1);
759
760        pt.basek = self.basek();
761        pt.k = pt.k().min(self.k());
762    }
763}
764
765pub trait GLWECiphertextToRef {
766    fn to_ref(&self) -> GLWECiphertext<&[u8]>;
767}
768
769impl<D: AsRef<[u8]>> GLWECiphertextToRef for GLWECiphertext<D> {
770    fn to_ref(&self) -> GLWECiphertext<&[u8]> {
771        GLWECiphertext {
772            data: self.data.to_ref(),
773            basek: self.basek,
774            k: self.k,
775        }
776    }
777}
778
779pub trait GLWECiphertextToMut {
780    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
781}
782
783impl<D: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextToMut for GLWECiphertext<D> {
784    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
785        GLWECiphertext {
786            data: self.data.to_mut(),
787            basek: self.basek,
788            k: self.k,
789        }
790    }
791}
792
793impl<D> GLWEOps for GLWECiphertext<D>
794where
795    D: AsRef<[u8]> + AsMut<[u8]>,
796    GLWECiphertext<D>: GLWECiphertextToMut + Infos + SetMetaData,
797{
798}