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