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