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