poulpy_core/layouts/
glwe_ct.rs

1use poulpy_hal::{
2    layouts::{
3        Data, DataMut, DataRef, FillUniform, ReaderFrom, ToOwnedDeep, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo, ZnxInfos,
4    },
5    source::Source,
6};
7
8use crate::layouts::{Base2K, BuildError, Degree, LWEInfos, Rank, TorusPrecision};
9use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
10use std::fmt;
11
12pub trait GLWEInfos
13where
14    Self: LWEInfos,
15{
16    fn rank(&self) -> Rank;
17    fn glwe_layout(&self) -> GLWECiphertextLayout {
18        GLWECiphertextLayout {
19            n: self.n(),
20            base2k: self.base2k(),
21            k: self.k(),
22            rank: self.rank(),
23        }
24    }
25}
26
27pub trait GLWELayoutSet {
28    fn set_k(&mut self, k: TorusPrecision);
29    fn set_basek(&mut self, base2k: Base2K);
30}
31
32#[derive(PartialEq, Eq, Copy, Clone, Debug)]
33pub struct GLWECiphertextLayout {
34    pub n: Degree,
35    pub base2k: Base2K,
36    pub k: TorusPrecision,
37    pub rank: Rank,
38}
39
40impl LWEInfos for GLWECiphertextLayout {
41    fn n(&self) -> Degree {
42        self.n
43    }
44
45    fn base2k(&self) -> Base2K {
46        self.base2k
47    }
48
49    fn k(&self) -> TorusPrecision {
50        self.k
51    }
52}
53
54impl GLWEInfos for GLWECiphertextLayout {
55    fn rank(&self) -> Rank {
56        self.rank
57    }
58}
59
60#[derive(PartialEq, Eq, Clone)]
61pub struct GLWECiphertext<D: Data> {
62    pub(crate) data: VecZnx<D>,
63    pub(crate) base2k: Base2K,
64    pub(crate) k: TorusPrecision,
65}
66
67impl<D: DataMut> GLWELayoutSet for GLWECiphertext<D> {
68    fn set_basek(&mut self, base2k: Base2K) {
69        self.base2k = base2k
70    }
71
72    fn set_k(&mut self, k: TorusPrecision) {
73        self.k = k
74    }
75}
76
77impl<D: DataRef> GLWECiphertext<D> {
78    pub fn data(&self) -> &VecZnx<D> {
79        &self.data
80    }
81}
82
83impl<D: DataMut> GLWECiphertext<D> {
84    pub fn data_mut(&mut self) -> &mut VecZnx<D> {
85        &mut self.data
86    }
87}
88
89pub struct GLWECiphertextBuilder<D: Data> {
90    data: Option<VecZnx<D>>,
91    base2k: Option<Base2K>,
92    k: Option<TorusPrecision>,
93}
94
95impl<D: Data> GLWECiphertext<D> {
96    #[inline]
97    pub fn builder() -> GLWECiphertextBuilder<D> {
98        GLWECiphertextBuilder {
99            data: None,
100            base2k: None,
101            k: None,
102        }
103    }
104}
105
106impl GLWECiphertextBuilder<Vec<u8>> {
107    #[inline]
108    pub fn layout<A>(mut self, layout: &A) -> Self
109    where
110        A: GLWEInfos,
111    {
112        self.data = Some(VecZnx::alloc(
113            layout.n().into(),
114            (layout.rank() + 1).into(),
115            layout.size(),
116        ));
117        self.base2k = Some(layout.base2k());
118        self.k = Some(layout.k());
119        self
120    }
121}
122
123impl<D: Data> GLWECiphertextBuilder<D> {
124    #[inline]
125    pub fn data(mut self, data: VecZnx<D>) -> Self {
126        self.data = Some(data);
127        self
128    }
129    #[inline]
130    pub fn base2k(mut self, base2k: Base2K) -> Self {
131        self.base2k = Some(base2k);
132        self
133    }
134    #[inline]
135    pub fn k(mut self, k: TorusPrecision) -> Self {
136        self.k = Some(k);
137        self
138    }
139
140    pub fn build(self) -> Result<GLWECiphertext<D>, BuildError> {
141        let data: VecZnx<D> = self.data.ok_or(BuildError::MissingData)?;
142        let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
143        let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
144
145        if base2k == 0_u32 {
146            return Err(BuildError::ZeroBase2K);
147        }
148
149        if k == 0_u32 {
150            return Err(BuildError::ZeroTorusPrecision);
151        }
152
153        if data.n() == 0 {
154            return Err(BuildError::ZeroDegree);
155        }
156
157        if data.cols() == 0 {
158            return Err(BuildError::ZeroCols);
159        }
160
161        if data.size() == 0 {
162            return Err(BuildError::ZeroLimbs);
163        }
164
165        Ok(GLWECiphertext { data, base2k, k })
166    }
167}
168
169impl<D: Data> LWEInfos for GLWECiphertext<D> {
170    fn base2k(&self) -> Base2K {
171        self.base2k
172    }
173
174    fn k(&self) -> TorusPrecision {
175        self.k
176    }
177
178    fn n(&self) -> Degree {
179        Degree(self.data.n() as u32)
180    }
181
182    fn size(&self) -> usize {
183        self.data.size()
184    }
185}
186
187impl<D: Data> GLWEInfos for GLWECiphertext<D> {
188    fn rank(&self) -> Rank {
189        Rank(self.data.cols() as u32 - 1)
190    }
191}
192
193impl<D: DataRef> ToOwnedDeep for GLWECiphertext<D> {
194    type Owned = GLWECiphertext<Vec<u8>>;
195    fn to_owned_deep(&self) -> Self::Owned {
196        GLWECiphertext {
197            data: self.data.to_owned_deep(),
198            k: self.k,
199            base2k: self.base2k,
200        }
201    }
202}
203
204impl<D: DataRef> fmt::Debug for GLWECiphertext<D> {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        write!(f, "{self}")
207    }
208}
209
210impl<D: DataRef> fmt::Display for GLWECiphertext<D> {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        write!(
213            f,
214            "GLWECiphertext: base2k={} k={}: {}",
215            self.base2k().0,
216            self.k().0,
217            self.data
218        )
219    }
220}
221
222impl<D: DataMut> FillUniform for GLWECiphertext<D> {
223    fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
224        self.data.fill_uniform(log_bound, source);
225    }
226}
227
228impl GLWECiphertext<Vec<u8>> {
229    pub fn alloc<A>(infos: &A) -> Self
230    where
231        A: GLWEInfos,
232    {
233        Self::alloc_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
234    }
235
236    pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
237        Self {
238            data: VecZnx::alloc(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize),
239            base2k,
240            k,
241        }
242    }
243
244    pub fn alloc_bytes<A>(infos: &A) -> usize
245    where
246        A: GLWEInfos,
247    {
248        Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
249    }
250
251    pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize {
252        VecZnx::alloc_bytes(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
253    }
254}
255
256pub trait GLWECiphertextToRef {
257    fn to_ref(&self) -> GLWECiphertext<&[u8]>;
258}
259
260impl<D: DataRef> GLWECiphertextToRef for GLWECiphertext<D> {
261    fn to_ref(&self) -> GLWECiphertext<&[u8]> {
262        GLWECiphertext::builder()
263            .k(self.k())
264            .base2k(self.base2k())
265            .data(self.data.to_ref())
266            .build()
267            .unwrap()
268    }
269}
270
271pub trait GLWECiphertextToMut {
272    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
273}
274
275impl<D: DataMut> GLWECiphertextToMut for GLWECiphertext<D> {
276    fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
277        GLWECiphertext::builder()
278            .k(self.k())
279            .base2k(self.base2k())
280            .data(self.data.to_mut())
281            .build()
282            .unwrap()
283    }
284}
285
286impl<D: DataMut> ReaderFrom for GLWECiphertext<D> {
287    fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
288        self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
289        self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
290        self.data.read_from(reader)
291    }
292}
293
294impl<D: DataRef> WriterTo for GLWECiphertext<D> {
295    fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
296        writer.write_u32::<LittleEndian>(self.k.0)?;
297        writer.write_u32::<LittleEndian>(self.base2k.0)?;
298        self.data.write_to(writer)
299    }
300}