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