poulpy/poulpy-core/src/layouts/ggsw_ct.rs
poulpy-core/src/layouts/ggsw_ct.rsRaw
1use poulpy_hal::{
2 layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, MatZnxToMut, MatZnxToRef, ReaderFrom, WriterTo, ZnxInfos},
3 source::Source,
4};
5use std::fmt;
6
7use crate::layouts::{Base2K, BuildError, Degree, Dnum, Dsize, GLWECiphertext, GLWEInfos, LWEInfos, Rank, TorusPrecision};
8
9pub trait GGSWInfos
10where
11 Self: GLWEInfos,
12{
13 fn dnum(&self) -> Dnum;
14 fn dsize(&self) -> Dsize;
15 fn ggsw_layout(&self) -> GGSWCiphertextLayout {
16 GGSWCiphertextLayout {
17 n: self.n(),
18 base2k: self.base2k(),
19 k: self.k(),
20 rank: self.rank(),
21 dnum: self.dnum(),
22 dsize: self.dsize(),
23 }
24 }
25}
26
27#[derive(PartialEq, Eq, Copy, Clone, Debug)]
28pub struct GGSWCiphertextLayout {
29 pub n: Degree,
30 pub base2k: Base2K,
31 pub k: TorusPrecision,
32 pub rank: Rank,
33 pub dnum: Dnum,
34 pub dsize: Dsize,
35}
36
37impl LWEInfos for GGSWCiphertextLayout {
38 fn base2k(&self) -> Base2K {
39 self.base2k
40 }
41
42 fn k(&self) -> TorusPrecision {
43 self.k
44 }
45
46 fn n(&self) -> Degree {
47 self.n
48 }
49}
50impl GLWEInfos for GGSWCiphertextLayout {
51 fn rank(&self) -> Rank {
52 self.rank
53 }
54}
55
56impl GGSWInfos for GGSWCiphertextLayout {
57 fn dsize(&self) -> Dsize {
58 self.dsize
59 }
60
61 fn dnum(&self) -> Dnum {
62 self.dnum
63 }
64}
65
66#[derive(PartialEq, Eq, Clone)]
67pub struct GGSW<D: Data> {
68 pub(crate) data: MatZnx<D>,
69 pub(crate) k: TorusPrecision,
70 pub(crate) base2k: Base2K,
71 pub(crate) dsize: Dsize,
72}
73
74impl<D: Data> LWEInfos for GGSW<D> {
75 fn n(&self) -> Degree {
76 Degree(self.data.n() as u32)
77 }
78
79 fn base2k(&self) -> Base2K {
80 self.base2k
81 }
82
83 fn k(&self) -> TorusPrecision {
84 self.k
85 }
86
87 fn size(&self) -> usize {
88 self.data.size()
89 }
90}
91
92impl<D: Data> GLWEInfos for GGSW<D> {
93 fn rank(&self) -> Rank {
94 Rank(self.data.cols_out() as u32 - 1)
95 }
96}
97
98impl<D: Data> GGSWInfos for GGSW<D> {
99 fn dsize(&self) -> Dsize {
100 self.dsize
101 }
102
103 fn dnum(&self) -> Dnum {
104 Dnum(self.data.rows() as u32)
105 }
106}
107
108pub struct GGSWCiphertextBuilder<D: Data> {
109 data: Option<MatZnx<D>>,
110 base2k: Option<Base2K>,
111 k: Option<TorusPrecision>,
112 dsize: Option<Dsize>,
113}
114
115impl<D: Data> GGSW<D> {
116 #[inline]
117 pub fn builder() -> GGSWCiphertextBuilder<D> {
118 GGSWCiphertextBuilder {
119 data: None,
120 base2k: None,
121 k: None,
122 dsize: None,
123 }
124 }
125}
126
127impl GGSWCiphertextBuilder<Vec<u8>> {
128 #[inline]
129 pub fn layout<A>(mut self, infos: &A) -> Self
130 where
131 A: GGSWInfos,
132 {
133 debug_assert!(
134 infos.size() as u32 > infos.dsize().0,
135 "invalid ggsw: ceil(k/base2k): {} <= dsize: {}",
136 infos.size(),
137 infos.dsize()
138 );
139
140 assert!(
141 infos.dnum().0 * infos.dsize().0 <= infos.size() as u32,
142 "invalid ggsw: dnum: {} * dsize:{} > ceil(k/base2k): {}",
143 infos.dnum(),
144 infos.dsize(),
145 infos.size(),
146 );
147
148 self.data = Some(MatZnx::alloc(
149 infos.n().into(),
150 infos.dnum().into(),
151 (infos.rank() + 1).into(),
152 (infos.rank() + 1).into(),
153 infos.size(),
154 ));
155 self.base2k = Some(infos.base2k());
156 self.k = Some(infos.k());
157 self.dsize = Some(infos.dsize());
158 self
159 }
160}
161
162impl<D: Data> GGSWCiphertextBuilder<D> {
163 #[inline]
164 pub fn data(mut self, data: MatZnx<D>) -> Self {
165 self.data = Some(data);
166 self
167 }
168 #[inline]
169 pub fn base2k(mut self, base2k: Base2K) -> Self {
170 self.base2k = Some(base2k);
171 self
172 }
173 #[inline]
174 pub fn k(mut self, k: TorusPrecision) -> Self {
175 self.k = Some(k);
176 self
177 }
178
179 #[inline]
180 pub fn dsize(mut self, dsize: Dsize) -> Self {
181 self.dsize = Some(dsize);
182 self
183 }
184
185 pub fn build(self) -> Result<GGSW<D>, BuildError> {
186 let data: MatZnx<D> = self.data.ok_or(BuildError::MissingData)?;
187 let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
188 let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
189 let dsize: Dsize = self.dsize.ok_or(BuildError::MissingDigits)?;
190
191 if base2k == 0_u32 {
192 return Err(BuildError::ZeroBase2K);
193 }
194
195 if dsize == 0_u32 {
196 return Err(BuildError::ZeroBase2K);
197 }
198
199 if k == 0_u32 {
200 return Err(BuildError::ZeroTorusPrecision);
201 }
202
203 if data.n() == 0 {
204 return Err(BuildError::ZeroDegree);
205 }
206
207 if data.cols() == 0 {
208 return Err(BuildError::ZeroCols);
209 }
210
211 if data.size() == 0 {
212 return Err(BuildError::ZeroLimbs);
213 }
214
215 Ok(GGSW {
216 data,
217 base2k,
218 k,
219 dsize,
220 })
221 }
222}
223
224impl<D: DataRef> fmt::Debug for GGSW<D> {
225 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226 write!(f, "{}", self.data)
227 }
228}
229
230impl<D: DataRef> fmt::Display for GGSW<D> {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 write!(
233 f,
234 "(GGSWCiphertext: k: {} base2k: {} dsize: {}) {}",
235 self.k().0,
236 self.base2k().0,
237 self.dsize().0,
238 self.data
239 )
240 }
241}
242
243impl<D: DataMut> FillUniform for GGSW<D> {
244 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
245 self.data.fill_uniform(log_bound, source);
246 }
247}
248
249impl<D: DataRef> GGSW<D> {
250 pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
251 GLWECiphertext::builder()
252 .data(self.data.at(row, col))
253 .base2k(self.base2k())
254 .k(self.k())
255 .build()
256 .unwrap()
257 }
258}
259
260impl<D: DataMut> GGSW<D> {
261 pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
262 GLWECiphertext::builder()
263 .base2k(self.base2k())
264 .k(self.k())
265 .data(self.data.at_mut(row, col))
266 .build()
267 .unwrap()
268 }
269}
270
271impl GGSW<Vec<u8>> {
272 pub fn alloc<A>(infos: &A) -> Self
273 where
274 A: GGSWInfos,
275 {
276 Self::alloc_with(
277 infos.n(),
278 infos.base2k(),
279 infos.k(),
280 infos.rank(),
281 infos.dnum(),
282 infos.dsize(),
283 )
284 }
285
286 pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
287 let size: usize = k.0.div_ceil(base2k.0) as usize;
288 debug_assert!(
289 size as u32 > dsize.0,
290 "invalid ggsw: ceil(k/base2k): {size} <= dsize: {}",
291 dsize.0
292 );
293
294 assert!(
295 dnum.0 * dsize.0 <= size as u32,
296 "invalid ggsw: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
297 dnum.0,
298 dsize.0,
299 );
300
301 Self {
302 data: MatZnx::alloc(
303 n.into(),
304 dnum.into(),
305 (rank + 1).into(),
306 (rank + 1).into(),
307 k.0.div_ceil(base2k.0) as usize,
308 ),
309 k,
310 base2k,
311 dsize,
312 }
313 }
314
315 pub fn alloc_bytes<A>(infos: &A) -> usize
316 where
317 A: GGSWInfos,
318 {
319 Self::alloc_bytes_with(
320 infos.n(),
321 infos.base2k(),
322 infos.k(),
323 infos.rank(),
324 infos.dnum(),
325 infos.dsize(),
326 )
327 }
328
329 pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
330 let size: usize = k.0.div_ceil(base2k.0) as usize;
331 debug_assert!(
332 size as u32 > dsize.0,
333 "invalid ggsw: ceil(k/base2k): {size} <= dsize: {}",
334 dsize.0
335 );
336
337 assert!(
338 dnum.0 * dsize.0 <= size as u32,
339 "invalid ggsw: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
340 dnum.0,
341 dsize.0,
342 );
343
344 MatZnx::alloc_bytes(
345 n.into(),
346 dnum.into(),
347 (rank + 1).into(),
348 (rank + 1).into(),
349 k.0.div_ceil(base2k.0) as usize,
350 )
351 }
352}
353
354use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
355
356impl<D: DataMut> ReaderFrom for GGSW<D> {
357 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
358 self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
359 self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
360 self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
361 self.data.read_from(reader)
362 }
363}
364
365impl<D: DataRef> WriterTo for GGSW<D> {
366 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
367 writer.write_u32::<LittleEndian>(self.k.into())?;
368 writer.write_u32::<LittleEndian>(self.base2k.into())?;
369 writer.write_u32::<LittleEndian>(self.dsize.into())?;
370 self.data.write_to(writer)
371 }
372}
373
374pub trait GGSWToMut {
375 fn to_mut(&mut self) -> GGSW<&mut [u8]>;
376}
377
378impl<D: DataMut> GGSWToMut for GGSW<D> {
379 fn to_mut(&mut self) -> GGSW<&mut [u8]> {
380 GGSW {
381 dsize: self.dsize,
382 k: self.k,
383 base2k: self.base2k,
384 data: self.data.to_mut(),
385 }
386 }
387}
388
389pub trait GGSWToRef {
390 fn to_ref(&self) -> GGSW<&[u8]>;
391}
392
393impl<D: DataRef> GGSWToRef for GGSW<D> {
394 fn to_ref(&self) -> GGSW<&[u8]> {
395 GGSW {
396 dsize: self.dsize,
397 k: self.k,
398 base2k: self.base2k,
399 data: self.data.to_ref(),
400 }
401 }
402}