poulpy_core/layouts/compressed/
gglwe_ct.rs

1use poulpy_hal::{
2    api::{VecZnxCopy, VecZnxFillUniform},
3    layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
4    source::Source,
5};
6
7use crate::layouts::{
8    GGLWECiphertext, Infos,
9    compressed::{Decompress, GLWECiphertextCompressed},
10};
11use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
12use std::fmt;
13
14#[derive(PartialEq, Eq, Clone)]
15pub struct GGLWECiphertextCompressed<D: Data> {
16    pub(crate) data: MatZnx<D>,
17    pub(crate) basek: usize,
18    pub(crate) k: usize,
19    pub(crate) rank_out: usize,
20    pub(crate) digits: usize,
21    pub(crate) seed: Vec<[u8; 32]>,
22}
23
24impl<D: DataRef> fmt::Debug for GGLWECiphertextCompressed<D> {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        write!(f, "{}", self)
27    }
28}
29
30impl<D: DataMut> FillUniform for GGLWECiphertextCompressed<D> {
31    fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
32        self.data.fill_uniform(log_bound, source);
33    }
34}
35
36impl<D: DataMut> Reset for GGLWECiphertextCompressed<D>
37where
38    MatZnx<D>: Reset,
39{
40    fn reset(&mut self) {
41        self.data.reset();
42        self.basek = 0;
43        self.k = 0;
44        self.digits = 0;
45        self.rank_out = 0;
46        self.seed = Vec::new();
47    }
48}
49
50impl<D: DataRef> fmt::Display for GGLWECiphertextCompressed<D> {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(
53            f,
54            "(GGLWECiphertextCompressed: basek={} k={} digits={}) {}",
55            self.basek, self.k, self.digits, self.data
56        )
57    }
58}
59
60impl GGLWECiphertextCompressed<Vec<u8>> {
61    pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
62        let size: usize = k.div_ceil(basek);
63        debug_assert!(
64            size > digits,
65            "invalid gglwe: ceil(k/basek): {} <= digits: {}",
66            size,
67            digits
68        );
69
70        assert!(
71            rows * digits <= size,
72            "invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
73            rows,
74            digits,
75            size
76        );
77
78        Self {
79            data: MatZnx::alloc(n, rows, rank_in, 1, size),
80            basek,
81            k,
82            rank_out,
83            digits,
84            seed: vec![[0u8; 32]; rows * rank_in],
85        }
86    }
87
88    pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize {
89        let size: usize = k.div_ceil(basek);
90        debug_assert!(
91            size > digits,
92            "invalid gglwe: ceil(k/basek): {} <= digits: {}",
93            size,
94            digits
95        );
96
97        assert!(
98            rows * digits <= size,
99            "invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
100            rows,
101            digits,
102            size
103        );
104
105        MatZnx::alloc_bytes(n, rows, rank_in, 1, rows)
106    }
107}
108
109impl<D: Data> Infos for GGLWECiphertextCompressed<D> {
110    type Inner = MatZnx<D>;
111
112    fn inner(&self) -> &Self::Inner {
113        &self.data
114    }
115
116    fn basek(&self) -> usize {
117        self.basek
118    }
119
120    fn k(&self) -> usize {
121        self.k
122    }
123}
124
125impl<D: Data> GGLWECiphertextCompressed<D> {
126    pub fn rank(&self) -> usize {
127        self.rank_out
128    }
129
130    pub fn digits(&self) -> usize {
131        self.digits
132    }
133
134    pub fn rank_in(&self) -> usize {
135        self.data.cols_in()
136    }
137
138    pub fn rank_out(&self) -> usize {
139        self.rank_out
140    }
141}
142
143impl<D: DataRef> GGLWECiphertextCompressed<D> {
144    pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
145        GLWECiphertextCompressed {
146            data: self.data.at(row, col),
147            basek: self.basek,
148            k: self.k,
149            rank: self.rank_out,
150            seed: self.seed[self.rank_in() * row + col],
151        }
152    }
153}
154
155impl<D: DataMut> GGLWECiphertextCompressed<D> {
156    pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
157        let rank_in: usize = self.rank_in();
158        GLWECiphertextCompressed {
159            data: self.data.at_mut(row, col),
160            basek: self.basek,
161            k: self.k,
162            rank: self.rank_out,
163            seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
164        }
165    }
166}
167
168impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
169    fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
170        self.k = reader.read_u64::<LittleEndian>()? as usize;
171        self.basek = reader.read_u64::<LittleEndian>()? as usize;
172        self.digits = reader.read_u64::<LittleEndian>()? as usize;
173        self.rank_out = reader.read_u64::<LittleEndian>()? as usize;
174        let seed_len = reader.read_u64::<LittleEndian>()? as usize;
175        self.seed = vec![[0u8; 32]; seed_len];
176        for s in &mut self.seed {
177            reader.read_exact(s)?;
178        }
179        self.data.read_from(reader)
180    }
181}
182
183impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
184    fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
185        writer.write_u64::<LittleEndian>(self.k as u64)?;
186        writer.write_u64::<LittleEndian>(self.basek as u64)?;
187        writer.write_u64::<LittleEndian>(self.digits as u64)?;
188        writer.write_u64::<LittleEndian>(self.rank_out as u64)?;
189        writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
190        for s in &self.seed {
191            writer.write_all(s)?;
192        }
193        self.data.write_to(writer)
194    }
195}
196
197impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GGLWECiphertextCompressed<DR>> for GGLWECiphertext<D>
198where
199    Module<B>: VecZnxFillUniform + VecZnxCopy,
200{
201    fn decompress(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DR>) {
202        #[cfg(debug_assertions)]
203        {
204            use poulpy_hal::layouts::ZnxInfos;
205
206            assert_eq!(
207                self.n(),
208                other.data.n(),
209                "invalid receiver: self.n()={} != other.n()={}",
210                self.n(),
211                other.data.n()
212            );
213            assert_eq!(
214                self.size(),
215                other.size(),
216                "invalid receiver: self.size()={} != other.size()={}",
217                self.size(),
218                other.size()
219            );
220            assert_eq!(
221                self.rank_in(),
222                other.rank_in(),
223                "invalid receiver: self.rank_in()={} != other.rank_in()={}",
224                self.rank_in(),
225                other.rank_in()
226            );
227            assert_eq!(
228                self.rank_out(),
229                other.rank_out(),
230                "invalid receiver: self.rank_out()={} != other.rank_out()={}",
231                self.rank_out(),
232                other.rank_out()
233            );
234
235            assert_eq!(
236                self.rows(),
237                other.rows(),
238                "invalid receiver: self.rows()={} != other.rows()={}",
239                self.rows(),
240                other.rows()
241            );
242        }
243
244        let rank_in: usize = self.rank_in();
245        let rows: usize = self.rows();
246
247        (0..rank_in).for_each(|col_i| {
248            (0..rows).for_each(|row_i| {
249                self.at_mut(row_i, col_i)
250                    .decompress(module, &other.at(row_i, col_i));
251            });
252        });
253    }
254}