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