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