poulpy/core/src/ggsw_ciphertext.rs
core/src/ggsw_ciphertext.rsRaw
1use backend::{
2 Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnx, Scratch, VecZnxAlloc,
3 VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, ZnxInfos,
4 ZnxZero,
5};
6use sampling::source::Source;
7
8use crate::{
9 automorphism::AutomorphismKey,
10 elem::{GetRow, Infos, SetRow},
11 glwe_ciphertext::GLWECiphertext,
12 glwe_ciphertext_fourier::GLWECiphertextFourier,
13 glwe_plaintext::GLWEPlaintext,
14 keys::SecretKeyFourier,
15 keyswitch_key::GLWESwitchingKey,
16 tensor_key::TensorKey,
17 utils::derive_size,
18};
19
20pub struct GGSWCiphertext<C, B: Backend> {
21 pub data: MatZnxDft<C, B>,
22 pub basek: usize,
23 pub k: usize,
24}
25
26impl<B: Backend> GGSWCiphertext<Vec<u8>, B> {
27 pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
28 Self {
29 data: module.new_mat_znx_dft(rows, rank + 1, rank + 1, derive_size(basek, k)),
30 basek: basek,
31 k: k,
32 }
33 }
34}
35
36impl<T, B: Backend> Infos for GGSWCiphertext<T, B> {
37 type Inner = MatZnxDft<T, B>;
38
39 fn inner(&self) -> &Self::Inner {
40 &self.data
41 }
42
43 fn basek(&self) -> usize {
44 self.basek
45 }
46
47 fn k(&self) -> usize {
48 self.k
49 }
50}
51
52impl<T, B: Backend> GGSWCiphertext<T, B> {
53 pub fn rank(&self) -> usize {
54 self.data.cols_out() - 1
55 }
56}
57
58impl GGSWCiphertext<Vec<u8>, FFT64> {
59 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, rank: usize, size: usize) -> usize {
60 GLWECiphertext::encrypt_sk_scratch_space(module, size)
61 + module.bytes_of_vec_znx(rank + 1, size)
62 + module.bytes_of_vec_znx(1, size)
63 + module.bytes_of_vec_znx_dft(rank + 1, size)
64 }
65
66 pub(crate) fn expand_row_scratch_space(
67 module: &Module<FFT64>,
68 self_size: usize,
69 tensor_key_size: usize,
70 rank: usize,
71 ) -> usize {
72 let tmp_dft_i: usize = module.bytes_of_vec_znx_dft(rank + 1, tensor_key_size);
73 let tmp_dft_col_data: usize = module.bytes_of_vec_znx_dft(1, self_size);
74 let vmp: usize =
75 tmp_dft_col_data + module.vmp_apply_tmp_bytes(self_size, self_size, self_size, rank, rank, tensor_key_size);
76 let tmp_idft: usize = module.bytes_of_vec_znx_big(1, tensor_key_size);
77 let norm: usize = module.vec_znx_big_normalize_tmp_bytes();
78 tmp_dft_i + ((tmp_dft_col_data + vmp) | (tmp_idft + norm))
79 }
80
81 pub(crate) fn keyswitch_internal_col0_scratch_space(
82 module: &Module<FFT64>,
83 out_size: usize,
84 in_size: usize,
85 ksk_size: usize,
86 rank: usize,
87 ) -> usize {
88 GLWECiphertext::keyswitch_from_fourier_scratch_space(module, out_size, rank, in_size, rank, ksk_size)
89 + module.bytes_of_vec_znx_dft(rank + 1, in_size)
90 }
91
92 pub fn keyswitch_scratch_space(
93 module: &Module<FFT64>,
94 out_size: usize,
95 in_size: usize,
96 ksk_size: usize,
97 tensor_key_size: usize,
98 rank: usize,
99 ) -> usize {
100 let res_znx: usize = module.bytes_of_vec_znx(rank + 1, out_size);
101 let ci_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
102 let ks: usize = GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, out_size, in_size, ksk_size, rank);
103 let expand_rows: usize = GGSWCiphertext::expand_row_scratch_space(module, out_size, tensor_key_size, rank);
104 let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
105 res_znx + ci_dft + (ks | expand_rows | res_dft)
106 }
107
108 pub fn keyswitch_inplace_scratch_space(
109 module: &Module<FFT64>,
110 out_size: usize,
111 ksk_size: usize,
112 tensor_key_size: usize,
113 rank: usize,
114 ) -> usize {
115 GGSWCiphertext::keyswitch_scratch_space(module, out_size, out_size, ksk_size, tensor_key_size, rank)
116 }
117
118 pub fn automorphism_scratch_space(
119 module: &Module<FFT64>,
120 out_size: usize,
121 in_size: usize,
122 auto_key_size: usize,
123 tensor_key_size: usize,
124 rank: usize,
125 ) -> usize {
126 let cols: usize = rank + 1;
127 let res: usize = module.bytes_of_vec_znx(cols, out_size);
128 let res_dft: usize = module.bytes_of_vec_znx_dft(cols, out_size);
129 let ci_dft: usize = module.bytes_of_vec_znx_dft(cols, out_size);
130 let ks_internal: usize =
131 GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, out_size, in_size, auto_key_size, rank);
132 let expand: usize = GGSWCiphertext::expand_row_scratch_space(module, out_size, tensor_key_size, rank);
133 res + ci_dft + (ks_internal | expand | res_dft)
134 }
135
136 pub fn automorphism_inplace_scratch_space(
137 module: &Module<FFT64>,
138 out_size: usize,
139 auto_key_size: usize,
140 tensor_key_size: usize,
141 rank: usize,
142 ) -> usize {
143 GGSWCiphertext::automorphism_scratch_space(
144 module,
145 out_size,
146 out_size,
147 auto_key_size,
148 tensor_key_size,
149 rank,
150 )
151 }
152
153 pub fn external_product_scratch_space(
154 module: &Module<FFT64>,
155 out_size: usize,
156 in_size: usize,
157 ggsw_size: usize,
158 rank: usize,
159 ) -> usize {
160 let tmp_in: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size);
161 let tmp_out: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
162 let ggsw: usize = GLWECiphertextFourier::external_product_scratch_space(module, out_size, in_size, ggsw_size, rank);
163 tmp_in + tmp_out + ggsw
164 }
165
166 pub fn external_product_inplace_scratch_space(
167 module: &Module<FFT64>,
168 out_size: usize,
169 ggsw_size: usize,
170 rank: usize,
171 ) -> usize {
172 let tmp: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
173 let ggsw: usize = GLWECiphertextFourier::external_product_inplace_scratch_space(module, out_size, ggsw_size, rank);
174 tmp + ggsw
175 }
176}
177
178impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
179 pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
180 &mut self,
181 module: &Module<FFT64>,
182 pt: &ScalarZnx<DataPt>,
183 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
184 source_xa: &mut Source,
185 source_xe: &mut Source,
186 sigma: f64,
187 scratch: &mut Scratch,
188 ) {
189 #[cfg(debug_assertions)]
190 {
191 assert_eq!(self.rank(), sk_dft.rank());
192 assert_eq!(self.n(), module.n());
193 assert_eq!(pt.n(), module.n());
194 assert_eq!(sk_dft.n(), module.n());
195 }
196
197 let size: usize = self.size();
198 let basek: usize = self.basek();
199 let k: usize = self.k();
200 let cols: usize = self.rank() + 1;
201
202 let (tmp_znx_pt, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
203 let (tmp_znx_ct, scrach_2) = scratch_1.tmp_vec_znx(module, cols, size);
204
205 let mut vec_znx_pt: GLWEPlaintext<&mut [u8]> = GLWEPlaintext {
206 data: tmp_znx_pt,
207 basek: basek,
208 k: k,
209 };
210
211 let mut vec_znx_ct: GLWECiphertext<&mut [u8]> = GLWECiphertext {
212 data: tmp_znx_ct,
213 basek: basek,
214 k,
215 };
216
217 (0..self.rows()).for_each(|row_i| {
218 vec_znx_pt.data.zero();
219
220 // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
221 module.vec_znx_add_scalar_inplace(&mut vec_znx_pt.data, 0, row_i, pt, 0);
222 module.vec_znx_normalize_inplace(basek, &mut vec_znx_pt.data, 0, scrach_2);
223
224 (0..cols).for_each(|col_j| {
225 // rlwe encrypt of vec_znx_pt into vec_znx_ct
226
227 vec_znx_ct.encrypt_sk_private(
228 module,
229 Some((&vec_znx_pt, col_j)),
230 sk_dft,
231 source_xa,
232 source_xe,
233 sigma,
234 scrach_2,
235 );
236
237 // Switch vec_znx_ct into DFT domain
238 {
239 let (mut vec_znx_dft_ct, _) = scrach_2.tmp_vec_znx_dft(module, cols, size);
240
241 (0..cols).for_each(|i| {
242 module.vec_znx_dft(&mut vec_znx_dft_ct, i, &vec_znx_ct.data, i);
243 });
244
245 module.vmp_prepare_row(&mut self.data, row_i, col_j, &vec_znx_dft_ct);
246 }
247 });
248 });
249 }
250
251 pub(crate) fn expand_row<R, DataCi: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
252 &mut self,
253 module: &Module<FFT64>,
254 col_j: usize,
255 res: &mut R,
256 ci_dft: &VecZnxDft<DataCi, FFT64>,
257 tsk: &TensorKey<DataTsk, FFT64>,
258 scratch: &mut Scratch,
259 ) where
260 R: VecZnxToMut,
261 {
262 let cols: usize = self.rank() + 1;
263
264 assert!(scratch.available() >= GGSWCiphertext::expand_row_scratch_space(module, self.size(), tsk.size(), tsk.rank()));
265
266 // Example for rank 3:
267 //
268 // Note: M is a vector (m, Bm, B^2m, B^3m, ...), so each column is
269 // actually composed of that many rows and we focus on a specific row here
270 // implicitely given ci_dft.
271 //
272 // # Input
273 //
274 // col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
275 // col 1: (0, 0, 0, 0)
276 // col 2: (0, 0, 0, 0)
277 // col 3: (0, 0, 0, 0)
278 //
279 // # Output
280 //
281 // col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
282 // col 1: (-(b0s0 + b1s1 + b2s2) , b0 + M[i], b1 , b2 )
283 // col 2: (-(c0s0 + c1s1 + c2s2) , c0 , c1 + M[i], c2 )
284 // col 3: (-(d0s0 + d1s1 + d2s2) , d0 , d1 , d2 + M[i])
285
286 let (mut tmp_dft_i, scratch1) = scratch.tmp_vec_znx_dft(module, cols, tsk.size());
287 {
288 let (mut tmp_dft_col_data, scratch2) = scratch1.tmp_vec_znx_dft(module, 1, self.size());
289
290 // Performs a key-switch for each combination of s[i]*s[j], i.e. for a0, a1, a2
291 //
292 // # Example for col=1
293 //
294 // a0 * (-(f0s0 + f1s1 + f1s2) + s0^2, f0, f1, f2) = (-(a0f0s0 + a0f1s1 + a0f1s2) + a0s0^2, a0f0, a0f1, a0f2)
295 // +
296 // a1 * (-(g0s0 + g1s1 + g1s2) + s0s1, g0, g1, g2) = (-(a1g0s0 + a1g1s1 + a1g1s2) + a1s0s1, a1g0, a1g1, a1g2)
297 // +
298 // a2 * (-(h0s0 + h1s1 + h1s2) + s0s2, h0, h1, h2) = (-(a2h0s0 + a2h1s1 + a2h1s2) + a2s0s2, a2h0, a2h1, a2h2)
299 // =
300 // (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0, x1, x2)
301 (1..cols).for_each(|col_i| {
302 // Extracts a[i] and multipies with Enc(s[i]s[j])
303 tmp_dft_col_data.extract_column(0, ci_dft, col_i);
304
305 if col_i == 1 {
306 module.vmp_apply(
307 &mut tmp_dft_i,
308 &tmp_dft_col_data,
309 &tsk.at(col_i - 1, col_j - 1).0.data, // Selects Enc(s[i]s[j])
310 scratch2,
311 );
312 } else {
313 module.vmp_apply_add(
314 &mut tmp_dft_i,
315 &tmp_dft_col_data,
316 &tsk.at(col_i - 1, col_j - 1).0.data, // Selects Enc(s[i]s[j])
317 scratch2,
318 );
319 }
320 });
321 }
322
323 // Adds -(sum a[i] * s[i]) + m) on the i-th column of tmp_idft_i
324 //
325 // (-(x0s0 + x1s1 + x2s2) + a0s0s0 + a1s0s1 + a2s0s2, x0, x1, x2)
326 // +
327 // (0, -(a0s0 + a1s1 + a2s2) + M[i], 0, 0)
328 // =
329 // (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0 -(a0s0 + a1s1 + a2s2) + M[i], x1, x2)
330 // =
331 // (-(x0s0 + x1s1 + x2s2), x0 + M[i], x1, x2)
332 module.vec_znx_dft_add_inplace(&mut tmp_dft_i, col_j, ci_dft, 0);
333 let (mut tmp_idft, scratch2) = scratch1.tmp_vec_znx_big(module, 1, tsk.size());
334 (0..cols).for_each(|i| {
335 module.vec_znx_idft_tmp_a(&mut tmp_idft, 0, &mut tmp_dft_i, i);
336 module.vec_znx_big_normalize(self.basek(), res, i, &tmp_idft, 0, scratch2);
337 });
338 }
339
340 pub fn keyswitch<DataLhs: AsRef<[u8]>, DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
341 &mut self,
342 module: &Module<FFT64>,
343 lhs: &GGSWCiphertext<DataLhs, FFT64>,
344 ksk: &GLWESwitchingKey<DataKsk, FFT64>,
345 tsk: &TensorKey<DataTsk, FFT64>,
346 scratch: &mut Scratch,
347 ) {
348 let cols: usize = self.rank() + 1;
349
350 let (res_data, scratch1) = scratch.tmp_vec_znx(&module, cols, self.size());
351 let mut res: GLWECiphertext<&mut [u8]> = GLWECiphertext::<&mut [u8]> {
352 data: res_data,
353 basek: self.basek(),
354 k: self.k(),
355 };
356
357 let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
358
359 // Keyswitch the j-th row of the col 0
360 (0..lhs.rows()).for_each(|row_i| {
361 // Key-switch column 0, i.e.
362 // col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0s0' + a1s1' + a2s2') + M[i], a0, a1, a2)
363 lhs.keyswitch_internal_col0(module, row_i, &mut res, ksk, scratch2);
364
365 // Isolates DFT(a[i])
366 (0..cols).for_each(|col_i| {
367 module.vec_znx_dft(&mut ci_dft, col_i, &res.data, col_i);
368 });
369
370 module.vmp_prepare_row(&mut self.data, row_i, 0, &ci_dft);
371
372 // Generates
373 //
374 // col 1: (-(b0s0' + b1s1' + b2s2') , b0 + M[i], b1 , b2 )
375 // col 2: (-(c0s0' + c1s1' + c2s2') , c0 , c1 + M[i], c2 )
376 // col 3: (-(d0s0' + d1s1' + d2s2') , d0 , d1 , d2 + M[i])
377 (1..cols).for_each(|col_j| {
378 self.expand_row(module, col_j, &mut res.data, &ci_dft, tsk, scratch2);
379
380 let (mut res_dft, _) = scratch2.tmp_vec_znx_dft(module, cols, self.size());
381 (0..cols).for_each(|i| {
382 module.vec_znx_dft(&mut res_dft, i, &res.data, i);
383 });
384
385 module.vmp_prepare_row(&mut self.data, row_i, col_j, &res_dft);
386 });
387 })
388 }
389
390 pub fn keyswitch_inplace<DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
391 &mut self,
392 module: &Module<FFT64>,
393 ksk: &GLWESwitchingKey<DataKsk, FFT64>,
394 tsk: &TensorKey<DataTsk, FFT64>,
395 scratch: &mut Scratch,
396 ) {
397 unsafe {
398 let self_ptr: *mut GGSWCiphertext<DataSelf, FFT64> = self as *mut GGSWCiphertext<DataSelf, FFT64>;
399 self.keyswitch(module, &*self_ptr, ksk, tsk, scratch);
400 }
401 }
402
403 pub fn automorphism<DataLhs: AsRef<[u8]>, DataAk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
404 &mut self,
405 module: &Module<FFT64>,
406 lhs: &GGSWCiphertext<DataLhs, FFT64>,
407 auto_key: &AutomorphismKey<DataAk, FFT64>,
408 tensor_key: &TensorKey<DataTsk, FFT64>,
409 scratch: &mut Scratch,
410 ) {
411 #[cfg(debug_assertions)]
412 {
413 assert_eq!(
414 self.rank(),
415 lhs.rank(),
416 "ggsw_out rank: {} != ggsw_in rank: {}",
417 self.rank(),
418 lhs.rank()
419 );
420 assert_eq!(
421 self.rank(),
422 auto_key.rank(),
423 "ggsw_in rank: {} != auto_key rank: {}",
424 self.rank(),
425 auto_key.rank()
426 );
427 assert_eq!(
428 self.rank(),
429 tensor_key.rank(),
430 "ggsw_in rank: {} != tensor_key rank: {}",
431 self.rank(),
432 tensor_key.rank()
433 );
434 assert!(
435 scratch.available()
436 >= GGSWCiphertext::automorphism_scratch_space(
437 module,
438 self.size(),
439 lhs.size(),
440 auto_key.size(),
441 tensor_key.size(),
442 self.rank()
443 )
444 )
445 };
446
447 let cols: usize = self.rank() + 1;
448
449 let (res_data, scratch1) = scratch.tmp_vec_znx(&module, cols, self.size());
450 let mut res: GLWECiphertext<&mut [u8]> = GLWECiphertext::<&mut [u8]> {
451 data: res_data,
452 basek: self.basek(),
453 k: self.k(),
454 };
455
456 let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, self.size());
457
458 // Keyswitch the j-th row of the col 0
459 (0..lhs.rows()).for_each(|row_i| {
460 // Key-switch column 0, i.e.
461 // col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2)
462 lhs.keyswitch_internal_col0(module, row_i, &mut res, &auto_key.key, scratch2);
463
464 // Isolates DFT(AUTO(a[i]))
465 (0..cols).for_each(|col_i| {
466 // (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2) -> (-(a0s0 + a1s1 + a2s2) + pi(M[i]), a0, a1, a2)
467 module.vec_znx_automorphism_inplace(auto_key.p(), &mut res.data, col_i);
468 module.vec_znx_dft(&mut ci_dft, col_i, &res.data, col_i);
469 });
470
471 module.vmp_prepare_row(&mut self.data, row_i, 0, &ci_dft);
472
473 // Generates
474 //
475 // col 1: (-(b0s0 + b1s1 + b2s2) , b0 + pi(M[i]), b1 , b2 )
476 // col 2: (-(c0s0 + c1s1 + c2s2) , c0 , c1 + pi(M[i]), c2 )
477 // col 3: (-(d0s0 + d1s1 + d2s2) , d0 , d1 , d2 + pi(M[i]))
478 (1..cols).for_each(|col_j| {
479 self.expand_row(module, col_j, &mut res.data, &ci_dft, tensor_key, scratch2);
480
481 let (mut res_dft, _) = scratch2.tmp_vec_znx_dft(module, cols, self.size());
482 (0..cols).for_each(|i| {
483 module.vec_znx_dft(&mut res_dft, i, &res.data, i);
484 });
485
486 module.vmp_prepare_row(&mut self.data, row_i, col_j, &res_dft);
487 });
488 })
489 }
490
491 pub fn automorphism_inplace<DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
492 &mut self,
493 module: &Module<FFT64>,
494 auto_key: &AutomorphismKey<DataKsk, FFT64>,
495 tensor_key: &TensorKey<DataTsk, FFT64>,
496 scratch: &mut Scratch,
497 ) {
498 unsafe {
499 let self_ptr: *mut GGSWCiphertext<DataSelf, FFT64> = self as *mut GGSWCiphertext<DataSelf, FFT64>;
500 self.automorphism(module, &*self_ptr, auto_key, tensor_key, scratch);
501 }
502 }
503
504 pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
505 &mut self,
506 module: &Module<FFT64>,
507 lhs: &GGSWCiphertext<DataLhs, FFT64>,
508 rhs: &GGSWCiphertext<DataRhs, FFT64>,
509 scratch: &mut Scratch,
510 ) {
511 #[cfg(debug_assertions)]
512 {
513 assert_eq!(
514 self.rank(),
515 lhs.rank(),
516 "ggsw_out rank: {} != ggsw_in rank: {}",
517 self.rank(),
518 lhs.rank()
519 );
520 assert_eq!(
521 self.rank(),
522 rhs.rank(),
523 "ggsw_in rank: {} != ggsw_apply rank: {}",
524 self.rank(),
525 rhs.rank()
526 );
527 }
528
529 let (tmp_in_data, scratch1) = scratch.tmp_vec_znx_dft(module, lhs.rank() + 1, lhs.size());
530
531 let mut tmp_in: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier::<&mut [u8], FFT64> {
532 data: tmp_in_data,
533 basek: lhs.basek(),
534 k: lhs.k(),
535 };
536
537 let (tmp_out_data, scratch2) = scratch1.tmp_vec_znx_dft(module, self.rank() + 1, self.size());
538
539 let mut tmp_out: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier::<&mut [u8], FFT64> {
540 data: tmp_out_data,
541 basek: self.basek(),
542 k: self.k(),
543 };
544
545 (0..self.rank() + 1).for_each(|col_i| {
546 (0..self.rows()).for_each(|row_j| {
547 lhs.get_row(module, row_j, col_i, &mut tmp_in);
548 tmp_out.external_product(module, &tmp_in, rhs, scratch2);
549 self.set_row(module, row_j, col_i, &tmp_out);
550 });
551 });
552
553 tmp_out.data.zero();
554
555 (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
556 (0..self.rank() + 1).for_each(|col_j| {
557 self.set_row(module, row_i, col_j, &tmp_out);
558 });
559 });
560 }
561
562 pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
563 &mut self,
564 module: &Module<FFT64>,
565 rhs: &GGSWCiphertext<DataRhs, FFT64>,
566 scratch: &mut Scratch,
567 ) {
568 #[cfg(debug_assertions)]
569 {
570 assert_eq!(
571 self.rank(),
572 rhs.rank(),
573 "ggsw_out rank: {} != ggsw_apply: {}",
574 self.rank(),
575 rhs.rank()
576 );
577 }
578
579 let (tmp_data, scratch1) = scratch.tmp_vec_znx_dft(module, self.rank() + 1, self.size());
580
581 let mut tmp: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier::<&mut [u8], FFT64> {
582 data: tmp_data,
583 basek: self.basek(),
584 k: self.k(),
585 };
586
587 (0..self.rank() + 1).for_each(|col_i| {
588 (0..self.rows()).for_each(|row_j| {
589 self.get_row(module, row_j, col_i, &mut tmp);
590 tmp.external_product_inplace(module, rhs, scratch1);
591 self.set_row(module, row_j, col_i, &tmp);
592 });
593 });
594 }
595}
596
597impl<DataSelf: AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
598 pub(crate) fn keyswitch_internal_col0<DataRes: AsMut<[u8]> + AsRef<[u8]>, DataKsk: AsRef<[u8]>>(
599 &self,
600 module: &Module<FFT64>,
601 row_i: usize,
602 res: &mut GLWECiphertext<DataRes>,
603 ksk: &GLWESwitchingKey<DataKsk, FFT64>,
604 scratch: &mut Scratch,
605 ) {
606 #[cfg(debug_assertions)]
607 {
608 assert_eq!(self.rank(), ksk.rank());
609 assert_eq!(res.rank(), ksk.rank());
610 assert!(
611 scratch.available()
612 >= GGSWCiphertext::keyswitch_internal_col0_scratch_space(
613 module,
614 res.size(),
615 self.size(),
616 ksk.size(),
617 ksk.rank()
618 )
619 )
620 }
621
622 let (tmp_dft_in_data, scratch2) = scratch.tmp_vec_znx_dft(module, self.rank() + 1, self.size());
623 let mut tmp_dft_in: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier::<&mut [u8], FFT64> {
624 data: tmp_dft_in_data,
625 basek: self.basek(),
626 k: self.k(),
627 };
628 self.get_row(module, row_i, 0, &mut tmp_dft_in);
629 res.keyswitch_from_fourier(module, &tmp_dft_in, ksk, scratch2);
630 }
631}
632
633impl<DataSelf: AsRef<[u8]>> GetRow<FFT64> for GGSWCiphertext<DataSelf, FFT64> {
634 fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
635 &self,
636 module: &Module<FFT64>,
637 row_i: usize,
638 col_j: usize,
639 res: &mut GLWECiphertextFourier<R, FFT64>,
640 ) {
641 module.vmp_extract_row(&mut res.data, &self.data, row_i, col_j);
642 }
643}
644
645impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGSWCiphertext<DataSelf, FFT64> {
646 fn set_row<R: AsRef<[u8]>>(
647 &mut self,
648 module: &Module<FFT64>,
649 row_i: usize,
650 col_j: usize,
651 a: &GLWECiphertextFourier<R, FFT64>,
652 ) {
653 module.vmp_prepare_row(&mut self.data, row_i, col_j, &a.data);
654 }
655}