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