1use poulpy_hal::{
2 layouts::{
3 Backend, Data, FillUniform, HostDataMut, HostDataRef, ReaderFrom, ToOwnedDeep, VecZnx, VecZnxToBackendMut,
4 VecZnxToBackendRef, WriterTo,
5 },
6 source::Source,
7};
8
9use crate::layouts::{Base2K, Degree, LWEInfos, Rank, SetBase2k, SetK, TorusPrecision};
10use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
11use poulpy_hal::layouts::ZnxWord;
12use std::fmt;
13
14pub trait GLWEInfos
20where
21 Self: LWEInfos,
22{
23 fn rank(&self) -> Rank;
25 fn glwe_layout(&self) -> GLWELayout {
27 GLWELayout {
28 n: self.n(),
29 base2k: self.base2k(),
30 k: self.k(),
31 rank: self.rank(),
32 }
33 }
34}
35
36impl<T: GLWEInfos + ?Sized> GLWEInfos for &T {
37 fn rank(&self) -> Rank {
38 (**self).rank()
39 }
40}
41
42impl<T: GLWEInfos + ?Sized> GLWEInfos for &mut T {
43 fn rank(&self) -> Rank {
44 (**self).rank()
45 }
46}
47
48#[derive(PartialEq, Eq, Copy, Clone, Debug)]
50pub struct GLWELayout {
51 pub n: Degree,
53 pub base2k: Base2K,
55 pub k: TorusPrecision,
57 pub rank: Rank,
59}
60
61impl LWEInfos for GLWELayout {
62 fn n(&self) -> Degree {
63 self.n
64 }
65
66 fn base2k(&self) -> Base2K {
67 self.base2k
68 }
69
70 fn max_size(&self) -> usize {
71 self.k.div_ceil(self.base2k) as usize
72 }
73
74 fn k(&self) -> TorusPrecision {
75 self.k
76 }
77}
78
79impl GLWEInfos for GLWELayout {
80 fn rank(&self) -> Rank {
81 self.rank
82 }
83}
84
85#[derive(PartialEq, Eq, Clone)]
92pub struct GLWE<D: Data, W: ZnxWord> {
93 pub(crate) data: VecZnx<D, W>,
94 pub(crate) k: TorusPrecision,
95 pub(crate) base2k: Base2K,
96}
97
98pub type GLWEBackendRef<'a, BE> = GLWE<<BE as Backend>::BufRef<'a>, <BE as Backend>::ZnxWord>;
99pub type GLWEBackendMut<'a, BE> = GLWE<<BE as Backend>::BufMut<'a>, <BE as Backend>::ZnxWord>;
100
101impl<D: Data, W: ZnxWord> SetBase2k for GLWE<D, W> {
102 fn set_base2k(&mut self, base2k: Base2K) {
103 self.base2k = base2k
104 }
105}
106
107impl<D: Data, W: ZnxWord> SetBase2k for &mut GLWE<D, W> {
108 fn set_base2k(&mut self, base2k: Base2K) {
109 self.base2k = base2k
110 }
111}
112
113impl<D: Data, W: ZnxWord> SetK for GLWE<D, W> {
114 fn set_k(&mut self, k: TorusPrecision) {
115 self.k = k
116 }
117}
118
119impl<D: Data, W: ZnxWord> SetK for &mut GLWE<D, W> {
120 fn set_k(&mut self, k: TorusPrecision) {
121 self.k = k
122 }
123}
124
125impl<D: Data, W: ZnxWord> GLWE<D, W> {
126 pub fn data(&self) -> &VecZnx<D, W> {
128 &self.data
129 }
130}
131
132impl<D: Data, W: ZnxWord> GLWE<D, W> {
133 pub fn data_mut(&mut self) -> &mut VecZnx<D, W> {
135 &mut self.data
136 }
137}
138
139impl<D: Data, W: ZnxWord> LWEInfos for GLWE<D, W> {
140 fn base2k(&self) -> Base2K {
141 self.base2k
142 }
143
144 fn n(&self) -> Degree {
145 Degree(self.data.n() as u32)
146 }
147
148 fn max_size(&self) -> usize {
149 self.data.size()
150 }
151
152 fn k(&self) -> TorusPrecision {
153 self.k
154 }
155}
156
157impl<D: Data, W: ZnxWord> GLWEInfos for GLWE<D, W> {
158 fn rank(&self) -> Rank {
159 Rank(self.data.cols() as u32 - 1)
160 }
161}
162
163impl<D: HostDataRef, W: ZnxWord> ToOwnedDeep for GLWE<D, W> {
164 type Owned = GLWE<Vec<u8>, W>;
165 fn to_owned_deep(&self) -> Self::Owned {
166 GLWE {
167 data: self.data.to_owned_deep(),
168 base2k: self.base2k,
169 k: self.k,
170 }
171 }
172}
173
174impl<D: Data, W: ZnxWord> GLWE<D, W> {
175 pub fn to_host_owned<BE>(&self) -> GLWE<Vec<u8>, W>
177 where
178 BE: Backend<OwnedBuf = D, ZnxWord = W>,
179 {
180 GLWE {
181 data: self.data.to_host_owned::<BE>(),
182 base2k: self.base2k,
183 k: self.k,
184 }
185 }
186
187 pub fn display_host<BE>(&self) -> String
189 where
190 BE: Backend<OwnedBuf = D, ZnxWord = W>,
191 {
192 self.to_host_owned::<BE>().to_string()
193 }
194}
195
196impl<D: Data, W: ZnxWord> GLWE<D, W> {
197 pub fn reinterpret<To>(self) -> GLWE<To::OwnedBuf, To::ZnxWord>
199 where
200 To: Backend<OwnedBuf = D, ZnxWord = W>,
201 {
202 let shape = self.data.shape();
203 let data = self.data.into_data();
204 GLWE {
205 data: VecZnx::from_data(data, shape.n(), shape.cols(), shape.size()),
206 base2k: self.base2k,
207 k: self.k,
208 }
209 }
210}
211
212impl<D: HostDataRef, W: ZnxWord> fmt::Debug for GLWE<D, W> {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 write!(f, "{self}")
215 }
216}
217
218impl<D: HostDataRef, W: ZnxWord> fmt::Display for GLWE<D, W> {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "GLWE: base2k={} k={}: {}", self.base2k().0, self.k().0, self.data)
221 }
222}
223
224impl<D: HostDataMut, W: ZnxWord> FillUniform for GLWE<D, W> {
225 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
226 self.data.fill_uniform(log_bound, source);
227 }
228}
229
230#[expect(
231 dead_code,
232 reason = "host-owned constructors are kept for serialization and host-only staging"
233)]
234impl<W: ZnxWord> GLWE<Vec<u8>, W> {
235 pub(crate) fn alloc_from_infos<A>(infos: &A) -> Self
237 where
238 A: GLWEInfos,
239 {
240 Self::alloc(infos.n(), infos.base2k(), infos.k(), infos.rank())
241 }
242
243 pub(crate) fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
250 let size: usize = k.0.div_ceil(base2k.0) as usize;
251 GLWE {
252 data: VecZnx::from_data(
253 poulpy_hal::layouts::HostBytesBackend::alloc_bytes(VecZnx::<Vec<u8>, W>::bytes_of(
254 n.into(),
255 (rank + 1).into(),
256 size,
257 )),
258 n.into(),
259 (rank + 1).into(),
260 size,
261 ),
262 base2k,
263 k,
264 }
265 }
266
267 pub fn bytes_of_from_infos<A>(infos: &A) -> usize
269 where
270 A: GLWEInfos,
271 {
272 Self::bytes_of(infos.n(), infos.base2k(), infos.k(), infos.rank())
273 }
274
275 pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize {
282 VecZnx::<Vec<u8>, W>::bytes_of(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
283 }
284}
285
286impl<D: HostDataMut, W: ZnxWord> ReaderFrom for GLWE<D, W> {
287 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
289 self.set_base2k(Base2K(reader.read_u32::<LittleEndian>()?));
290 self.data.read_from(reader)?;
291 Ok(())
292 }
293}
294
295impl<D: HostDataRef, W: ZnxWord> WriterTo for GLWE<D, W> {
296 fn write_to<Wr: std::io::Write>(&self, writer: &mut Wr) -> std::io::Result<()> {
298 writer.write_u32::<LittleEndian>(self.base2k.0)?;
299 self.data.write_to(writer)
300 }
301}
302
303pub trait GLWEToBackendRef<BE: Backend>: Sized {
304 fn to_backend_ref(&self) -> GLWEBackendRef<'_, BE>;
305}
306
307impl<BE: Backend, D: Data> GLWEToBackendRef<BE> for GLWE<D, BE::ZnxWord>
308where
309 VecZnx<D, BE::ZnxWord>: VecZnxToBackendRef<BE>,
310{
311 fn to_backend_ref(&self) -> GLWEBackendRef<'_, BE> {
312 GLWE {
313 base2k: self.base2k,
314 k: self.k,
315 data: self.data.to_backend_ref(),
316 }
317 }
318}
319
320pub fn glwe_backend_ref_from_ref<'a, 'b, BE: Backend>(glwe: &'a GLWE<BE::BufRef<'b>, BE::ZnxWord>) -> GLWEBackendRef<'a, BE> {
321 GLWE {
322 base2k: glwe.base2k,
323 k: glwe.k,
324 data: poulpy_hal::layouts::vec_znx_backend_ref_from_ref::<BE>(&glwe.data),
325 }
326}
327
328impl<'b, BE: Backend + 'b> GLWEToBackendRef<BE> for &GLWE<BE::BufRef<'b>, BE::ZnxWord> {
329 fn to_backend_ref(&self) -> GLWEBackendRef<'_, BE> {
330 glwe_backend_ref_from_ref::<BE>(self)
331 }
332}
333
334pub fn glwe_backend_ref_from_mut<'a, 'b, BE: Backend>(glwe: &'a GLWE<BE::BufMut<'b>, BE::ZnxWord>) -> GLWEBackendRef<'a, BE> {
335 GLWE {
336 base2k: glwe.base2k,
337 k: glwe.k,
338 data: poulpy_hal::layouts::vec_znx_backend_ref_from_mut::<BE>(&glwe.data),
339 }
340}
341
342pub trait GLWEToBackendMut<BE: Backend>: GLWEToBackendRef<BE> {
343 fn to_backend_mut(&mut self) -> GLWEBackendMut<'_, BE>;
344}
345
346impl<BE: Backend, D: Data> GLWEToBackendMut<BE> for GLWE<D, BE::ZnxWord>
347where
348 VecZnx<D, BE::ZnxWord>: VecZnxToBackendRef<BE> + VecZnxToBackendMut<BE>,
349{
350 fn to_backend_mut(&mut self) -> GLWEBackendMut<'_, BE> {
351 GLWE {
352 base2k: self.base2k,
353 k: self.k,
354 data: self.data.to_backend_mut(),
355 }
356 }
357}
358
359impl<'b, BE: Backend + 'b> GLWEToBackendRef<BE> for &mut GLWE<BE::BufMut<'b>, BE::ZnxWord> {
360 fn to_backend_ref(&self) -> GLWEBackendRef<'_, BE> {
361 glwe_backend_ref_from_mut::<BE>(self)
362 }
363}
364
365impl<'b, BE: Backend + 'b> GLWEToBackendMut<BE> for &mut GLWE<BE::BufMut<'b>, BE::ZnxWord> {
366 fn to_backend_mut(&mut self) -> GLWEBackendMut<'_, BE> {
367 glwe_backend_mut_from_mut::<BE>(self)
368 }
369}
370
371pub fn glwe_backend_mut_from_mut<'a, 'b, BE: Backend>(glwe: &'a mut GLWE<BE::BufMut<'b>, BE::ZnxWord>) -> GLWEBackendMut<'a, BE> {
372 GLWE {
373 base2k: glwe.base2k,
374 k: glwe.k,
375 data: poulpy_hal::layouts::vec_znx_backend_mut_from_mut::<BE>(&mut glwe.data),
376 }
377}