poulpy_core/layouts/
gglwe_ct.rs

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