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}