arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/rlwe/src/elem_rlwe.rs
rlwe/src/elem_rlwe.rsRaw
  1use base2k::{
  2    AddNormal, Backend, FFT64, FillUniform, Module, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps,
  3    ScalarZnxDftToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc,
  4    VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps, VecZnxToMut, VecZnxToRef,
  5};
  6use sampling::source::Source;
  7
  8use crate::{
  9    elem::Infos,
 10    keys::{PublicKey, SecretDistribution, SecretKeyDft},
 11    utils::derive_size,
 12};
 13
 14pub struct RLWECt<C> {
 15    pub data: VecZnx<C>,
 16    pub log_base2k: usize,
 17    pub log_k: usize,
 18}
 19
 20impl RLWECt<Vec<u8>> {
 21    pub fn new<B: Backend>(module: &Module<B>, log_base2k: usize, log_k: usize, cols: usize) -> Self {
 22        Self {
 23            data: module.new_vec_znx(cols, derive_size(log_base2k, log_k)),
 24            log_base2k: log_base2k,
 25            log_k: log_k,
 26        }
 27    }
 28}
 29
 30impl<T> Infos for RLWECt<T> {
 31    type Inner = VecZnx<T>;
 32
 33    fn inner(&self) -> &Self::Inner {
 34        &self.data
 35    }
 36
 37    fn log_base2k(&self) -> usize {
 38        self.log_base2k
 39    }
 40
 41    fn log_k(&self) -> usize {
 42        self.log_k
 43    }
 44}
 45
 46impl<C> VecZnxToMut for RLWECt<C>
 47where
 48    VecZnx<C>: VecZnxToMut,
 49{
 50    fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
 51        self.data.to_mut()
 52    }
 53}
 54
 55impl<C> VecZnxToRef for RLWECt<C>
 56where
 57    VecZnx<C>: VecZnxToRef,
 58{
 59    fn to_ref(&self) -> VecZnx<&[u8]> {
 60        self.data.to_ref()
 61    }
 62}
 63
 64pub struct RLWEPt<C> {
 65    pub data: VecZnx<C>,
 66    pub log_base2k: usize,
 67    pub log_k: usize,
 68}
 69
 70impl<T> Infos for RLWEPt<T> {
 71    type Inner = VecZnx<T>;
 72
 73    fn inner(&self) -> &Self::Inner {
 74        &self.data
 75    }
 76
 77    fn log_base2k(&self) -> usize {
 78        self.log_base2k
 79    }
 80
 81    fn log_k(&self) -> usize {
 82        self.log_k
 83    }
 84}
 85
 86impl<C> VecZnxToMut for RLWEPt<C>
 87where
 88    VecZnx<C>: VecZnxToMut,
 89{
 90    fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
 91        self.data.to_mut()
 92    }
 93}
 94
 95impl<C> VecZnxToRef for RLWEPt<C>
 96where
 97    VecZnx<C>: VecZnxToRef,
 98{
 99    fn to_ref(&self) -> VecZnx<&[u8]> {
100        self.data.to_ref()
101    }
102}
103
104impl RLWEPt<Vec<u8>> {
105    pub fn new<B: Backend>(module: &Module<B>, log_base2k: usize, log_k: usize) -> Self {
106        Self {
107            data: module.new_vec_znx(1, derive_size(log_base2k, log_k)),
108            log_base2k: log_base2k,
109            log_k: log_k,
110        }
111    }
112}
113
114pub struct RLWECtDft<C, B: Backend> {
115    pub data: VecZnxDft<C, B>,
116    pub log_base2k: usize,
117    pub log_k: usize,
118}
119
120impl<B: Backend> RLWECtDft<Vec<u8>, B> {
121    pub fn new(module: &Module<B>, log_base2k: usize, log_k: usize, cols: usize) -> Self {
122        Self {
123            data: module.new_vec_znx_dft(cols, derive_size(log_base2k, log_k)),
124            log_base2k: log_base2k,
125            log_k: log_k,
126        }
127    }
128}
129
130impl<T, B: Backend> Infos for RLWECtDft<T, B> {
131    type Inner = VecZnxDft<T, B>;
132
133    fn inner(&self) -> &Self::Inner {
134        &self.data
135    }
136
137    fn log_base2k(&self) -> usize {
138        self.log_base2k
139    }
140
141    fn log_k(&self) -> usize {
142        self.log_k
143    }
144}
145
146impl<C, B: Backend> VecZnxDftToMut<B> for RLWECtDft<C, B>
147where
148    VecZnxDft<C, B>: VecZnxDftToMut<B>,
149{
150    fn to_mut(&mut self) -> VecZnxDft<&mut [u8], B> {
151        self.data.to_mut()
152    }
153}
154
155impl<C, B: Backend> VecZnxDftToRef<B> for RLWECtDft<C, B>
156where
157    VecZnxDft<C, B>: VecZnxDftToRef<B>,
158{
159    fn to_ref(&self) -> VecZnxDft<&[u8], B> {
160        self.data.to_ref()
161    }
162}
163
164impl RLWECt<Vec<u8>> {
165    pub fn encrypt_sk_scratch_bytes<B: Backend>(module: &Module<B>, size: usize) -> usize {
166        (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
167    }
168
169    pub fn encrypt_pk_scratch_bytes<B: Backend>(module: &Module<B>, pk_size: usize) -> usize {
170        ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
171            + module.bytes_of_scalar_znx_dft(1)
172            + module.vec_znx_big_normalize_tmp_bytes()
173    }
174
175    pub fn decrypt_scratch_bytes<B: Backend>(module: &Module<B>, size: usize) -> usize {
176        (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
177    }
178}
179
180pub fn encrypt_rlwe_sk<C, P, S>(
181    module: &Module<FFT64>,
182    ct: &mut RLWECt<C>,
183    pt: Option<&RLWEPt<P>>,
184    sk_dft: &SecretKeyDft<S, FFT64>,
185    source_xa: &mut Source,
186    source_xe: &mut Source,
187    sigma: f64,
188    bound: f64,
189    scratch: &mut Scratch,
190) where
191    VecZnx<C>: VecZnxToMut + VecZnxToRef,
192    VecZnx<P>: VecZnxToRef,
193    ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
194{
195    let log_base2k: usize = ct.log_base2k();
196    let log_k: usize = ct.log_k();
197    let size: usize = ct.size();
198
199    // c1 = a
200    ct.data.fill_uniform(log_base2k, 1, size, source_xa);
201
202    let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, size);
203
204    {
205        let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, size);
206        module.vec_znx_dft(&mut c0_dft, 0, ct, 1);
207
208        // c0_dft = DFT(a) * DFT(s)
209        module.svp_apply_inplace(&mut c0_dft, 0, sk_dft, 0);
210
211        // c0_big = IDFT(c0_dft)
212        module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
213    }
214
215    // c0_big = m - c0_big
216    if let Some(pt) = pt {
217        module.vec_znx_big_sub_small_b_inplace(&mut c0_big, 0, pt, 0);
218    }
219    // c0_big += e
220    c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
221
222    // c0 = norm(c0_big = -as + m + e)
223    module.vec_znx_big_normalize(log_base2k, ct, 0, &c0_big, 0, scratch_1);
224}
225
226pub fn decrypt_rlwe<P, C, S>(
227    module: &Module<FFT64>,
228    pt: &mut RLWEPt<P>,
229    ct: &RLWECt<C>,
230    sk_dft: &SecretKeyDft<S, FFT64>,
231    scratch: &mut Scratch,
232) where
233    VecZnx<P>: VecZnxToMut + VecZnxToRef,
234    VecZnx<C>: VecZnxToRef,
235    ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
236{
237    let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, ct.size()); // TODO optimize size when pt << ct
238
239    {
240        let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, ct.size()); // TODO optimize size when pt << ct
241        module.vec_znx_dft(&mut c0_dft, 0, ct, 1);
242
243        // c0_dft = DFT(a) * DFT(s)
244        module.svp_apply_inplace(&mut c0_dft, 0, sk_dft, 0);
245
246        // c0_big = IDFT(c0_dft)
247        module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
248    }
249
250    // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
251    module.vec_znx_big_add_small_inplace(&mut c0_big, 0, ct, 0);
252
253    // pt = norm(BIG(m + e))
254    module.vec_znx_big_normalize(ct.log_base2k(), pt, 0, &mut c0_big, 0, scratch_1);
255
256    pt.log_base2k = ct.log_base2k();
257    pt.log_k = pt.log_k().min(ct.log_k());
258}
259
260impl<C> RLWECt<C> {
261    pub fn encrypt_sk<P, S>(
262        &mut self,
263        module: &Module<FFT64>,
264        pt: Option<&RLWEPt<P>>,
265        sk_dft: &SecretKeyDft<S, FFT64>,
266        source_xa: &mut Source,
267        source_xe: &mut Source,
268        sigma: f64,
269        bound: f64,
270        scratch: &mut Scratch,
271    ) where
272        VecZnx<C>: VecZnxToMut + VecZnxToRef,
273        VecZnx<P>: VecZnxToRef,
274        ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
275    {
276        encrypt_rlwe_sk(
277            module, self, pt, sk_dft, source_xa, source_xe, sigma, bound, scratch,
278        )
279    }
280
281    pub fn decrypt<P, S>(
282        &self,
283        module: &Module<FFT64>,
284        pt: &mut RLWEPt<P>,
285        sk_dft: &SecretKeyDft<S, FFT64>,
286        scratch: &mut Scratch,
287    ) where
288        VecZnx<P>: VecZnxToMut + VecZnxToRef,
289        VecZnx<C>: VecZnxToRef,
290        ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
291    {
292        decrypt_rlwe(module, pt, self, sk_dft, scratch);
293    }
294
295    pub fn encrypt_pk<P, S>(
296        &mut self,
297        module: &Module<FFT64>,
298        pt: Option<&RLWEPt<P>>,
299        pk: &PublicKey<S, FFT64>,
300        source_xu: &mut Source,
301        source_xe: &mut Source,
302        sigma: f64,
303        bound: f64,
304        scratch: &mut Scratch,
305    ) where
306        VecZnx<C>: VecZnxToMut + VecZnxToRef,
307        VecZnx<P>: VecZnxToRef,
308        VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
309    {
310        encrypt_rlwe_pk(
311            module, self, pt, pk, source_xu, source_xe, sigma, bound, scratch,
312        )
313    }
314}
315
316pub(crate) fn encrypt_zero_rlwe_dft_sk<C, S>(
317    module: &Module<FFT64>,
318    ct: &mut RLWECtDft<C, FFT64>,
319    sk: &SecretKeyDft<S, FFT64>,
320    source_xa: &mut Source,
321    source_xe: &mut Source,
322    sigma: f64,
323    bound: f64,
324    scratch: &mut Scratch,
325) where
326    VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
327    ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
328{
329    let log_base2k: usize = ct.log_base2k();
330    let log_k: usize = ct.log_k();
331    let size: usize = ct.size();
332
333    #[cfg(debug_assertions)]
334    {
335        match sk.dist {
336            SecretDistribution::NONE => panic!("invalid sk.dist = SecretDistribution::NONE"),
337            _ => {}
338        }
339        assert_eq!(ct.cols(), 2);
340    }
341
342    // ct[1] = DFT(a)
343    {
344        let (mut tmp_znx, _) = scratch.tmp_vec_znx(module, 1, size);
345        tmp_znx.fill_uniform(log_base2k, 0, size, source_xa);
346        module.vec_znx_dft(ct, 1, &tmp_znx, 0);
347    }
348
349    let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, size);
350
351    {
352        let (mut tmp_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, size);
353        // c0_dft = ct[1] * DFT(s)
354        module.svp_apply(&mut tmp_dft, 0, sk, 0, ct, 1);
355
356        // c0_big = IDFT(c0_dft)
357        module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut tmp_dft, 0);
358    }
359
360    // c0_big += e
361    c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
362
363    // c0 = norm(c0_big = -as - e), NOTE: e is centered at 0.
364    let (mut tmp_znx, scratch_2) = scratch_1.tmp_vec_znx(module, 1, size);
365    module.vec_znx_big_normalize(log_base2k, &mut tmp_znx, 0, &c0_big, 0, scratch_2);
366    module.vec_znx_negate_inplace(&mut tmp_znx, 0);
367    // ct[0] = DFT(-as + e)
368    module.vec_znx_dft(ct, 0, &tmp_znx, 0);
369}
370
371impl RLWECtDft<Vec<u8>, FFT64> {
372    pub fn encrypt_zero_sk_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
373        (module.bytes_of_vec_znx(1, size) | module.bytes_of_vec_znx_dft(1, size))
374            + module.bytes_of_vec_znx_big(1, size)
375            + module.bytes_of_vec_znx(1, size)
376            + module.vec_znx_big_normalize_tmp_bytes()
377    }
378
379    pub fn decrypt_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
380        (module.vec_znx_big_normalize_tmp_bytes()
381            | module.bytes_of_vec_znx_dft(1, size)
382            | (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes()))
383            + module.bytes_of_vec_znx_big(1, size)
384    }
385}
386
387pub fn decrypt_rlwe_dft<P, C, S>(
388    module: &Module<FFT64>,
389    pt: &mut RLWEPt<P>,
390    ct: &RLWECtDft<C, FFT64>,
391    sk: &SecretKeyDft<S, FFT64>,
392    scratch: &mut Scratch,
393) where
394    VecZnx<P>: VecZnxToMut + VecZnxToRef,
395    VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
396    ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
397{
398    let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, ct.size()); // TODO optimize size when pt << ct
399
400    {
401        let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, ct.size()); // TODO optimize size when pt << ct
402        // c0_dft = DFT(a) * DFT(s)
403        module.svp_apply(&mut c0_dft, 0, sk, 0, ct, 1);
404        // c0_big = IDFT(c0_dft)
405        module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
406    }
407
408    {
409        let (mut c1_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, ct.size());
410        // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
411        module.vec_znx_idft(&mut c1_big, 0, ct, 0, scratch_2);
412        module.vec_znx_big_add_inplace(&mut c0_big, 0, &c1_big, 0);
413    }
414
415    // pt = norm(BIG(m + e))
416    module.vec_znx_big_normalize(ct.log_base2k(), pt, 0, &mut c0_big, 0, scratch_1);
417
418    pt.log_base2k = ct.log_base2k();
419    pt.log_k = pt.log_k().min(ct.log_k());
420}
421
422impl<C> RLWECtDft<C, FFT64> {
423    pub(crate) fn encrypt_zero_sk<S>(
424        &mut self,
425        module: &Module<FFT64>,
426        sk_dft: &SecretKeyDft<S, FFT64>,
427        source_xa: &mut Source,
428        source_xe: &mut Source,
429        sigma: f64,
430        bound: f64,
431        scratch: &mut Scratch,
432    ) where
433        VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
434        ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
435    {
436        encrypt_zero_rlwe_dft_sk(
437            module, self, sk_dft, source_xa, source_xe, sigma, bound, scratch,
438        )
439    }
440
441    pub fn decrypt<P, S>(
442        &self,
443        module: &Module<FFT64>,
444        pt: &mut RLWEPt<P>,
445        sk_dft: &SecretKeyDft<S, FFT64>,
446        scratch: &mut Scratch,
447    ) where
448        VecZnx<P>: VecZnxToMut + VecZnxToRef,
449        VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
450        ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
451    {
452        decrypt_rlwe_dft(module, pt, self, sk_dft, scratch);
453    }
454}
455
456pub(crate) fn encrypt_rlwe_pk<C, P, S>(
457    module: &Module<FFT64>,
458    ct: &mut RLWECt<C>,
459    pt: Option<&RLWEPt<P>>,
460    pk: &PublicKey<S, FFT64>,
461    source_xu: &mut Source,
462    source_xe: &mut Source,
463    sigma: f64,
464    bound: f64,
465    scratch: &mut Scratch,
466) where
467    VecZnx<C>: VecZnxToMut + VecZnxToRef,
468    VecZnx<P>: VecZnxToRef,
469    VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
470{
471    #[cfg(debug_assertions)]
472    {
473        assert_eq!(ct.log_base2k(), pk.log_base2k());
474        assert_eq!(ct.n(), module.n());
475        assert_eq!(pk.n(), module.n());
476        if let Some(pt) = pt {
477            assert_eq!(pt.log_base2k(), pk.log_base2k());
478            assert_eq!(pt.n(), module.n());
479        }
480    }
481
482    let log_base2k: usize = pk.log_base2k();
483    let size_pk: usize = pk.size();
484
485    // Generates u according to the underlying secret distribution.
486    let (mut u_dft, scratch_1) = scratch.tmp_scalar_dft(module, 1);
487
488    {
489        let (mut u, _) = scratch_1.tmp_scalar(module, 1);
490        match pk.dist {
491            SecretDistribution::NONE => panic!(
492                "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through Self::generate"
493            ),
494            SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
495            SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
496        }
497
498        module.svp_prepare(&mut u_dft, 0, &u, 0);
499    }
500
501    let (mut tmp_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, size_pk); // TODO optimize size (e.g. when encrypting at low homomorphic capacity)
502    let (mut tmp_dft, scratch_3) = scratch_2.tmp_vec_znx_dft(module, 1, size_pk); // TODO optimize size (e.g. when encrypting at low homomorphic capacity)
503
504    // ct[0] = pk[0] * u + m + e0
505    module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 0);
506    module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
507    tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
508
509    if let Some(pt) = pt {
510        module.vec_znx_big_add_small_inplace(&mut tmp_big, 0, pt, 0);
511    }
512
513    module.vec_znx_big_normalize(log_base2k, ct, 0, &tmp_big, 0, scratch_3);
514
515    // ct[1] = pk[1] * u + e1
516    module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 1);
517    module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
518    tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
519    module.vec_znx_big_normalize(log_base2k, ct, 1, &tmp_big, 0, scratch_3);
520}
521
522#[cfg(test)]
523mod tests {
524    use base2k::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxZero};
525    use itertools::izip;
526    use sampling::source::Source;
527
528    use crate::{
529        elem_rlwe::{Infos, RLWECt, RLWECtDft, RLWEPt},
530        keys::{PublicKey, SecretKey, SecretKeyDft},
531    };
532
533    #[test]
534    fn encrypt_sk_fft64() {
535        let module: Module<FFT64> = Module::<FFT64>::new(32);
536        let log_base2k: usize = 8;
537        let log_k_ct: usize = 54;
538        let log_k_pt: usize = 30;
539
540        let sigma: f64 = 3.2;
541        let bound: f64 = sigma * 6.0;
542
543        let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
544        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_pt);
545
546        let mut source_xs: Source = Source::new([0u8; 32]);
547        let mut source_xe: Source = Source::new([0u8; 32]);
548        let mut source_xa: Source = Source::new([0u8; 32]);
549
550        let mut scratch: ScratchOwned = ScratchOwned::new(
551            RLWECt::encrypt_sk_scratch_bytes(&module, ct.size()) | RLWECt::decrypt_scratch_bytes(&module, ct.size()),
552        );
553
554        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
555        sk.fill_ternary_prob(0.5, &mut source_xs);
556
557        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
558        sk_dft.dft(&module, &sk);
559
560        let mut data_want: Vec<i64> = vec![0i64; module.n()];
561
562        data_want
563            .iter_mut()
564            .for_each(|x| *x = source_xa.next_i64() & 0xFF);
565
566        pt.data
567            .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
568
569        ct.encrypt_sk(
570            &module,
571            Some(&pt),
572            &sk_dft,
573            &mut source_xa,
574            &mut source_xe,
575            sigma,
576            bound,
577            scratch.borrow(),
578        );
579
580        pt.data.zero();
581
582        ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
583
584        let mut data_have: Vec<i64> = vec![0i64; module.n()];
585
586        pt.data
587            .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
588
589        // TODO: properly assert the decryption noise through std(dec(ct) - pt)
590        let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
591        izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
592            let b_scaled = (*b as f64) / scale;
593            assert!(
594                (*a as f64 - b_scaled).abs() < 0.1,
595                "{} {}",
596                *a as f64,
597                b_scaled
598            )
599        });
600
601        module.free();
602    }
603
604    #[test]
605    fn encrypt_zero_sk_fft64() {
606        let module: Module<FFT64> = Module::<FFT64>::new(1024);
607        let log_base2k: usize = 8;
608        let log_k_ct: usize = 55;
609
610        let sigma: f64 = 3.2;
611        let bound: f64 = sigma * 6.0;
612
613        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
614
615        let mut source_xs: Source = Source::new([0u8; 32]);
616        let mut source_xe: Source = Source::new([1u8; 32]);
617        let mut source_xa: Source = Source::new([0u8; 32]);
618
619        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
620        sk.fill_ternary_prob(0.5, &mut source_xs);
621        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
622        sk_dft.dft(&module, &sk);
623
624        let mut ct_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct, 2);
625
626        let mut scratch: ScratchOwned = ScratchOwned::new(
627            RLWECtDft::decrypt_scratch_bytes(&module, ct_dft.size())
628                | RLWECtDft::encrypt_zero_sk_scratch_bytes(&module, ct_dft.size()),
629        );
630
631        ct_dft.encrypt_zero_sk(
632            &module,
633            &sk_dft,
634            &mut source_xa,
635            &mut source_xe,
636            sigma,
637            bound,
638            scratch.borrow(),
639        );
640        ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
641
642        assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
643        module.free();
644    }
645
646    #[test]
647    fn encrypt_pk_fft64() {
648        let module: Module<FFT64> = Module::<FFT64>::new(32);
649        let log_base2k: usize = 8;
650        let log_k_ct: usize = 54;
651        let log_k_pk: usize = 64;
652
653        let sigma: f64 = 3.2;
654        let bound: f64 = sigma * 6.0;
655
656        let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
657        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
658
659        let mut source_xs: Source = Source::new([0u8; 32]);
660        let mut source_xe: Source = Source::new([0u8; 32]);
661        let mut source_xa: Source = Source::new([0u8; 32]);
662        let mut source_xu: Source = Source::new([0u8; 32]);
663
664        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
665        sk.fill_ternary_prob(0.5, &mut source_xs);
666        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
667        sk_dft.dft(&module, &sk);
668
669        let mut pk: PublicKey<Vec<u8>, FFT64> = PublicKey::new(&module, log_base2k, log_k_pk);
670        pk.generate(
671            &module,
672            &sk_dft,
673            &mut source_xa,
674            &mut source_xe,
675            sigma,
676            bound,
677        );
678
679        let mut scratch: ScratchOwned = ScratchOwned::new(
680            RLWECt::encrypt_sk_scratch_bytes(&module, ct.size())
681                | RLWECt::decrypt_scratch_bytes(&module, ct.size())
682                | RLWECt::encrypt_pk_scratch_bytes(&module, pk.size()),
683        );
684
685        let mut data_want: Vec<i64> = vec![0i64; module.n()];
686
687        data_want
688            .iter_mut()
689            .for_each(|x| *x = source_xa.next_i64() & 0);
690
691        pt_want
692            .data
693            .encode_vec_i64(0, log_base2k, log_k_ct, &data_want, 10);
694
695        ct.encrypt_pk(
696            &module,
697            Some(&pt_want),
698            &pk,
699            &mut source_xu,
700            &mut source_xe,
701            sigma,
702            bound,
703            scratch.borrow(),
704        );
705
706        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
707
708        ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
709
710        module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
711
712        assert!(((1.0f64 / 12.0).sqrt() - pt_want.data.std(0, log_base2k) * (log_k_ct as f64).exp2()).abs() < 0.2);
713
714        module.free();
715    }
716}