1use poulpy_hal::{
2 layouts::{
3 Backend, Data, FillUniform, HostDataMut, HostDataRef, MatZnx, MatZnxToBackendMut, MatZnxToBackendRef, Module, ReaderFrom,
4 WriterTo, mat_znx_at_backend_mut_from_mut, mat_znx_at_backend_ref_from_ref, mat_znx_backend_mut_from_mut,
5 mat_znx_backend_ref_from_mut,
6 },
7 source::Source,
8};
9
10use crate::layouts::{
11 Base2K, Degree, Dnum, Dsize, GGSWInfos, GGSWToBackendMut, GLWEInfos, LWEInfos, Rank, TorusPrecision,
12 compressed::{
13 GLWECompressed, GLWECompressedBackendMut, GLWECompressedBackendRef, GLWECompressedViewMut, GLWECompressedViewRef,
14 GLWEDecompress,
15 },
16};
17use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
18use poulpy_hal::layouts::ZnxWord;
19use std::{
20 fmt,
21 ops::{Deref, DerefMut},
22};
23
24#[derive(PartialEq, Eq, Clone)]
30pub struct GGSWCompressed<D: Data, W: ZnxWord> {
31 pub(crate) data: MatZnx<D, W>,
32 pub(crate) k_aux: TorusPrecision,
33 pub(crate) base2k: Base2K,
34 pub(crate) dsize: Dsize,
35 pub(crate) rank: Rank,
36 pub(crate) seed: Vec<[u8; 32]>,
37}
38
39pub struct GGSWCompressedBackendRef<'a, BE: Backend + 'a> {
40 inner: GGSWCompressed<BE::BufRef<'a>, BE::ZnxWord>,
41}
42
43impl<'a, BE: Backend + 'a> GGSWCompressedBackendRef<'a, BE> {
44 pub fn from_inner(inner: GGSWCompressed<BE::BufRef<'a>, BE::ZnxWord>) -> Self {
45 Self { inner }
46 }
47
48 pub fn into_inner(self) -> GGSWCompressed<BE::BufRef<'a>, BE::ZnxWord> {
49 self.inner
50 }
51
52 pub fn at_view(&self, row: usize, col: usize) -> GLWECompressedViewRef<'_, BE> {
53 GLWECompressedViewRef::from_inner(ggsw_compressed_at_backend_ref_from_ref::<BE>(&self.inner, row, col))
54 }
55}
56
57impl<'a, BE: Backend + 'a> Deref for GGSWCompressedBackendRef<'a, BE> {
58 type Target = GGSWCompressed<BE::BufRef<'a>, BE::ZnxWord>;
59
60 fn deref(&self) -> &Self::Target {
61 &self.inner
62 }
63}
64
65pub struct GGSWCompressedBackendMut<'a, BE: Backend + 'a> {
66 inner: GGSWCompressed<BE::BufMut<'a>, BE::ZnxWord>,
67}
68
69impl<'a, BE: Backend + 'a> GGSWCompressedBackendMut<'a, BE> {
70 pub fn from_inner(inner: GGSWCompressed<BE::BufMut<'a>, BE::ZnxWord>) -> Self {
71 Self { inner }
72 }
73
74 pub fn into_inner(self) -> GGSWCompressed<BE::BufMut<'a>, BE::ZnxWord> {
75 self.inner
76 }
77
78 pub fn at_view_mut(&mut self, row: usize, col: usize) -> GLWECompressedViewMut<'_, BE> {
79 GLWECompressedViewMut::from_inner(ggsw_compressed_at_backend_mut_from_mut::<BE>(&mut self.inner, row, col))
80 }
81}
82
83impl<'a, BE: Backend + 'a> Deref for GGSWCompressedBackendMut<'a, BE> {
84 type Target = GGSWCompressed<BE::BufMut<'a>, BE::ZnxWord>;
85
86 fn deref(&self) -> &Self::Target {
87 &self.inner
88 }
89}
90
91impl<'a, BE: Backend + 'a> DerefMut for GGSWCompressedBackendMut<'a, BE> {
92 fn deref_mut(&mut self) -> &mut Self::Target {
93 &mut self.inner
94 }
95}
96
97impl<'a, BE: Backend + 'a> LWEInfos for GGSWCompressedBackendRef<'a, BE> {
98 fn base2k(&self) -> Base2K {
99 self.inner.base2k()
100 }
101
102 fn n(&self) -> Degree {
103 self.inner.n()
104 }
105
106 fn max_size(&self) -> usize {
107 self.inner.max_size()
108 }
109
110 fn k(&self) -> TorusPrecision {
111 self.inner.k()
112 }
113}
114
115impl<'a, BE: Backend + 'a> GLWEInfos for GGSWCompressedBackendRef<'a, BE> {
116 fn rank(&self) -> Rank {
117 self.inner.rank()
118 }
119}
120
121impl<'a, BE: Backend + 'a> GGSWInfos for GGSWCompressedBackendRef<'a, BE> {
122 fn k_aux(&self) -> TorusPrecision {
123 self.inner.k_aux()
124 }
125
126 fn dnum(&self) -> Dnum {
127 self.inner.dnum()
128 }
129
130 fn dsize(&self) -> Dsize {
131 self.inner.dsize()
132 }
133}
134
135impl<'a, BE: Backend + 'a> LWEInfos for GGSWCompressedBackendMut<'a, BE> {
136 fn base2k(&self) -> Base2K {
137 self.inner.base2k()
138 }
139
140 fn n(&self) -> Degree {
141 self.inner.n()
142 }
143
144 fn max_size(&self) -> usize {
145 self.inner.max_size()
146 }
147
148 fn k(&self) -> TorusPrecision {
149 self.inner.k()
150 }
151}
152
153impl<'a, BE: Backend + 'a> GLWEInfos for GGSWCompressedBackendMut<'a, BE> {
154 fn rank(&self) -> Rank {
155 self.inner.rank()
156 }
157}
158
159impl<'a, BE: Backend + 'a> GGSWInfos for GGSWCompressedBackendMut<'a, BE> {
160 fn k_aux(&self) -> TorusPrecision {
161 self.inner.k_aux()
162 }
163
164 fn dnum(&self) -> Dnum {
165 self.inner.dnum()
166 }
167
168 fn dsize(&self) -> Dsize {
169 self.inner.dsize()
170 }
171}
172
173impl<'a, BE: Backend + 'a> GGSWCompressedSeedMut for GGSWCompressedBackendMut<'a, BE> {
174 fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
175 &mut self.inner.seed
176 }
177}
178
179pub trait GGSWCompressedSeedMut {
181 fn seed_mut(&mut self) -> &mut Vec<[u8; 32]>;
183}
184
185impl<D: Data, W: ZnxWord> GGSWCompressedSeedMut for GGSWCompressed<D, W> {
186 fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
187 &mut self.seed
188 }
189}
190
191pub trait GGSWCompressedSeed {
193 fn seed(&self) -> &Vec<[u8; 32]>;
195}
196
197impl<D: HostDataRef, W: ZnxWord> GGSWCompressedSeed for GGSWCompressed<D, W> {
198 fn seed(&self) -> &Vec<[u8; 32]> {
199 &self.seed
200 }
201}
202
203impl<D: Data, W: ZnxWord> LWEInfos for GGSWCompressed<D, W> {
204 fn n(&self) -> Degree {
205 Degree(self.data.n() as u32)
206 }
207
208 fn base2k(&self) -> Base2K {
209 self.base2k
210 }
211
212 fn max_size(&self) -> usize {
213 crate::layouts::key_size(self.base2k, self.dnum(), self.dsize, self.k_aux)
214 }
215
216 fn k(&self) -> TorusPrecision {
217 crate::layouts::key_k(self.base2k, self.dnum(), self.dsize, self.k_aux)
218 }
219}
220impl<D: Data, W: ZnxWord> GLWEInfos for GGSWCompressed<D, W> {
221 fn rank(&self) -> Rank {
222 self.rank
223 }
224}
225
226impl<D: Data, W: ZnxWord> GGSWInfos for GGSWCompressed<D, W> {
227 fn k_aux(&self) -> TorusPrecision {
228 self.k_aux
229 }
230
231 fn dsize(&self) -> Dsize {
232 self.dsize
233 }
234
235 fn dnum(&self) -> Dnum {
236 Dnum(self.data.rows() as u32)
237 }
238}
239
240impl<D: HostDataRef, W: ZnxWord> fmt::Debug for GGSWCompressed<D, W> {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 write!(f, "{}", self.data)
243 }
244}
245
246impl<D: HostDataRef, W: ZnxWord> fmt::Display for GGSWCompressed<D, W> {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 write!(
249 f,
250 "(GGSWCompressed: base2k={} k={} dsize={}) {}",
251 self.base2k,
252 self.k(),
253 self.dsize,
254 self.data
255 )
256 }
257}
258
259impl<D: HostDataMut, W: ZnxWord> FillUniform for GGSWCompressed<D, W> {
260 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
261 self.data.fill_uniform(log_bound, source);
262 }
263}
264
265impl<D: Data, W: ZnxWord> GGSWCompressed<D, W> {
266 pub(crate) fn alloc_from_infos<B: Backend<OwnedBuf = D, ZnxWord = W>, A>(infos: &A) -> Self
268 where
269 A: GGSWInfos,
270 {
271 Self::alloc::<B>(
272 infos.n(),
273 infos.base2k(),
274 infos.dnum(),
275 infos.dsize(),
276 infos.k_aux(),
277 infos.rank(),
278 )
279 }
280
281 pub(crate) fn alloc<B: Backend<OwnedBuf = D, ZnxWord = W>>(
283 n: Degree,
284 base2k: Base2K,
285 dnum: Dnum,
286 dsize: Dsize,
287 k_aux: TorusPrecision,
288 rank: Rank,
289 ) -> Self {
290 let size: usize = crate::layouts::key_size(base2k, dnum, dsize, k_aux);
291
292 GGSWCompressed {
293 data: MatZnx::from_data(
294 B::alloc_zeroed_bytes(B::bytes_of_mat_znx(n.into(), dnum.into(), (rank + 1).into(), 1, size)),
295 n.into(),
296 dnum.into(),
297 (rank + 1).into(),
298 1,
299 size,
300 ),
301 k_aux,
302 base2k,
303 dsize,
304 rank,
305 seed: vec![[0u8; 32]; dnum.as_usize() * (rank.as_usize() + 1)],
306 }
307 }
308
309 pub fn bytes_of_from_infos<A>(infos: &A) -> usize
311 where
312 A: GGSWInfos,
313 {
314 Self::bytes_of(
315 infos.n(),
316 infos.base2k(),
317 infos.dnum(),
318 infos.dsize(),
319 infos.k_aux(),
320 infos.rank(),
321 )
322 }
323
324 pub fn bytes_of(n: Degree, base2k: Base2K, dnum: Dnum, dsize: Dsize, k_aux: TorusPrecision, rank: Rank) -> usize {
326 let size: usize = crate::layouts::key_size(base2k, dnum, dsize, k_aux);
327
328 MatZnx::<Vec<u8>, W>::bytes_of(n.into(), dnum.into(), (rank + 1).into(), 1, size)
329 }
330}
331
332impl<D: HostDataRef, W: ZnxWord> GGSWCompressed<D, W> {
333 pub fn at(&self, row: usize, col: usize) -> GLWECompressed<&[u8], W> {
335 let rank: usize = self.rank().into();
336 GLWECompressed {
337 data: self.data.at(row, col),
338 k: self.k(),
339 base2k: self.base2k,
340 rank: self.rank,
341 seed: self.seed[row * (rank + 1) + col],
342 }
343 }
344}
345
346impl<D: HostDataMut, W: ZnxWord> GGSWCompressed<D, W> {
347 pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECompressed<&mut [u8], W> {
349 let rank: usize = self.rank().into();
350 let k = self.k();
351 let seed = self.seed[row * (rank + 1) + col];
352 GLWECompressed {
353 data: self.data.at_mut(row, col),
354 k,
355 base2k: self.base2k,
356 rank: self.rank,
357 seed,
358 }
359 }
360}
361
362impl<D: HostDataMut, W: ZnxWord> ReaderFrom for GGSWCompressed<D, W> {
363 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
364 self.k_aux = TorusPrecision(reader.read_u32::<LittleEndian>()?);
365 self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
366 self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
367 self.rank = Rank(reader.read_u32::<LittleEndian>()?);
368 let seed_len: usize = reader.read_u32::<LittleEndian>()? as usize;
369 self.seed = vec![[0u8; 32]; seed_len];
370 for s in &mut self.seed {
371 reader.read_exact(s)?;
372 }
373 self.data.read_from(reader)
374 }
375}
376
377impl<D: HostDataRef, W: ZnxWord> WriterTo for GGSWCompressed<D, W> {
378 fn write_to<Wr: std::io::Write>(&self, writer: &mut Wr) -> std::io::Result<()> {
379 writer.write_u32::<LittleEndian>(self.k_aux.into())?;
380 writer.write_u32::<LittleEndian>(self.base2k.into())?;
381 writer.write_u32::<LittleEndian>(self.dsize.into())?;
382 writer.write_u32::<LittleEndian>(self.rank.into())?;
383 writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
384 for s in &self.seed {
385 writer.write_all(s)?;
386 }
387 self.data.write_to(writer)
388 }
389}
390
391pub trait GGSWDecompress
396where
397 Self: GLWEDecompress,
398{
399 fn decompress_ggsw<R, O>(&self, res: &mut R, other: &O)
401 where
402 R: GGSWToBackendMut<Self::Backend> + GGSWInfos,
403 O: GGSWCompressedToBackendRef<Self::Backend> + GGSWInfos,
404 {
405 let mut res = res.to_backend_mut();
406 let other = other.to_backend_ref();
407
408 assert_eq!(res.rank(), other.rank());
409 let dnum: usize = res.dnum().into();
410 let rank: usize = res.rank().into();
411
412 for row_i in 0..dnum {
413 for col_j in 0..rank + 1 {
414 let mut dst = res.at_view_mut(row_i, col_j);
415 let src = other.at_view(row_i, col_j);
416 self.decompress_glwe(&mut dst, &src);
417 }
418 }
419 }
420}
421
422impl<B: Backend> GGSWDecompress for Module<B> where Self: GLWEDecompress {}
423
424pub trait GGSWCompressedToBackendRef<BE: Backend> {
427 fn to_backend_ref(&self) -> GGSWCompressedBackendRef<'_, BE>;
428}
429
430impl<BE: Backend> GGSWCompressedToBackendRef<BE> for GGSWCompressed<BE::OwnedBuf, BE::ZnxWord> {
431 fn to_backend_ref(&self) -> GGSWCompressedBackendRef<'_, BE> {
432 GGSWCompressedBackendRef::from_inner(GGSWCompressed {
433 k_aux: self.k_aux(),
434 base2k: self.base2k(),
435 dsize: self.dsize(),
436 rank: self.rank(),
437 seed: self.seed.clone(),
438 data: <MatZnx<BE::OwnedBuf, BE::ZnxWord> as MatZnxToBackendRef<BE>>::to_backend_ref(&self.data),
439 })
440 }
441}
442
443impl<'b, BE: Backend + 'b> GGSWCompressedToBackendRef<BE> for &GGSWCompressed<BE::BufRef<'b>, BE::ZnxWord> {
444 fn to_backend_ref(&self) -> GGSWCompressedBackendRef<'_, BE> {
445 GGSWCompressedBackendRef::from_inner(GGSWCompressed {
446 k_aux: self.k_aux(),
447 base2k: self.base2k(),
448 dsize: self.dsize(),
449 rank: self.rank(),
450 seed: self.seed.clone(),
451 data: poulpy_hal::layouts::mat_znx_backend_ref_from_ref::<BE>(&self.data),
452 })
453 }
454}
455
456impl<'b, BE: Backend + 'b> GGSWCompressedToBackendRef<BE> for &mut GGSWCompressed<BE::BufMut<'b>, BE::ZnxWord> {
457 fn to_backend_ref(&self) -> GGSWCompressedBackendRef<'_, BE> {
458 GGSWCompressedBackendRef::from_inner(GGSWCompressed {
459 k_aux: self.k_aux(),
460 base2k: self.base2k(),
461 dsize: self.dsize(),
462 rank: self.rank(),
463 seed: self.seed.clone(),
464 data: mat_znx_backend_ref_from_mut::<BE>(&self.data),
465 })
466 }
467}
468
469pub trait GGSWCompressedToBackendMut<BE: Backend>: GGSWCompressedToBackendRef<BE> {
470 fn to_backend_mut(&mut self) -> GGSWCompressedBackendMut<'_, BE>;
471}
472
473impl<BE: Backend> GGSWCompressedToBackendMut<BE> for GGSWCompressed<BE::OwnedBuf, BE::ZnxWord> {
474 fn to_backend_mut(&mut self) -> GGSWCompressedBackendMut<'_, BE> {
475 GGSWCompressedBackendMut::from_inner(GGSWCompressed {
476 k_aux: self.k_aux(),
477 base2k: self.base2k(),
478 dsize: self.dsize(),
479 rank: self.rank(),
480 seed: self.seed.clone(),
481 data: <MatZnx<BE::OwnedBuf, BE::ZnxWord> as MatZnxToBackendMut<BE>>::to_backend_mut(&mut self.data),
482 })
483 }
484}
485
486impl<'b, BE: Backend + 'b> GGSWCompressedToBackendMut<BE> for &mut GGSWCompressed<BE::BufMut<'b>, BE::ZnxWord> {
487 fn to_backend_mut(&mut self) -> GGSWCompressedBackendMut<'_, BE> {
488 GGSWCompressedBackendMut::from_inner(GGSWCompressed {
489 k_aux: self.k_aux(),
490 base2k: self.base2k(),
491 dsize: self.dsize(),
492 rank: self.rank(),
493 seed: self.seed.clone(),
494 data: mat_znx_backend_mut_from_mut::<BE>(&mut self.data),
495 })
496 }
497}
498
499fn ggsw_compressed_at_backend_mut_from_mut<'a, 'b, BE: Backend>(
500 ggsw: &'a mut GGSWCompressed<BE::BufMut<'b>, BE::ZnxWord>,
501 row: usize,
502 col: usize,
503) -> GLWECompressedBackendMut<'a, BE> {
504 let rank: usize = ggsw.rank().into();
505 let k = ggsw.k();
506 let seed = ggsw.seed[row * (rank + 1) + col];
507 let base2k = ggsw.base2k;
508 let rank_field = ggsw.rank;
509 GLWECompressed {
510 data: mat_znx_at_backend_mut_from_mut::<BE>(&mut ggsw.data, row, col),
511 k,
512 base2k,
513 rank: rank_field,
514 seed,
515 }
516}
517
518fn ggsw_compressed_at_backend_ref_from_ref<'a, 'b, BE: Backend>(
519 ggsw: &'a GGSWCompressed<BE::BufRef<'b>, BE::ZnxWord>,
520 row: usize,
521 col: usize,
522) -> GLWECompressedBackendRef<'a, BE> {
523 let rank: usize = ggsw.rank().into();
524 GLWECompressed {
525 data: mat_znx_at_backend_ref_from_ref::<BE>(&ggsw.data, row, col),
526 k: ggsw.k(),
527 base2k: ggsw.base2k,
528 rank: ggsw.rank,
529 seed: ggsw.seed[row * (rank + 1) + col],
530 }
531}