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