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