poulpy_core/layouts/compressed/
gglwe_ct.rs1use 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], }
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}