poulpy/base2k/src/znx_base.rs
base2k/src/znx_base.rsRaw
1use crate::{Backend, Module, alloc_aligned, assert_alignement, cast_mut};
2use itertools::izip;
3use std::cmp::min;
4
5pub struct ZnxBase {
6 /// The ring degree
7 pub n: usize,
8
9 /// The number of rows (in the third dimension)
10 pub rows: usize,
11
12 /// The number of polynomials
13 pub cols: usize,
14
15 /// The number of size per polynomial (a.k.a small polynomials).
16 pub size: usize,
17
18 /// Polynomial coefficients, as a contiguous array. Each col is equally spaced by n.
19 pub data: Vec<u8>,
20
21 /// Pointer to data (data can be enpty if [VecZnx] borrows space instead of owning it).
22 pub ptr: *mut u8,
23}
24
25impl ZnxBase {
26 pub fn from_bytes(n: usize, rows: usize, cols: usize, size: usize, mut bytes: Vec<u8>) -> Self {
27 let mut res: Self = Self::from_bytes_borrow(n, rows, cols, size, &mut bytes);
28 res.data = bytes;
29 res
30 }
31
32 pub fn from_bytes_borrow(n: usize, rows: usize, cols: usize, size: usize, bytes: &mut [u8]) -> Self {
33 #[cfg(debug_assertions)]
34 {
35 assert_eq!(n & (n - 1), 0, "n must be a power of two");
36 assert!(n > 0, "n must be greater than 0");
37 assert!(rows > 0, "rows must be greater than 0");
38 assert!(cols > 0, "cols must be greater than 0");
39 assert!(size > 0, "size must be greater than 0");
40 }
41 Self {
42 n: n,
43 rows: rows,
44 cols: cols,
45 size: size,
46 data: Vec::new(),
47 ptr: bytes.as_mut_ptr(),
48 }
49 }
50}
51
52pub trait GetZnxBase {
53 fn znx(&self) -> &ZnxBase;
54 fn znx_mut(&mut self) -> &mut ZnxBase;
55}
56
57pub trait ZnxInfos: GetZnxBase {
58 /// Returns the ring degree of the polynomials.
59 fn n(&self) -> usize {
60 self.znx().n
61 }
62
63 /// Returns the base two logarithm of the ring dimension of the polynomials.
64 fn log_n(&self) -> usize {
65 (usize::BITS - (self.n() - 1).leading_zeros()) as _
66 }
67
68 /// Returns the number of rows.
69 fn rows(&self) -> usize {
70 self.znx().rows
71 }
72 /// Returns the number of polynomials in each row.
73 fn cols(&self) -> usize {
74 self.znx().cols
75 }
76
77 /// Returns the number of size per polynomial.
78 fn size(&self) -> usize {
79 self.znx().size
80 }
81
82 /// Returns the underlying raw bytes array.
83 fn data(&self) -> &[u8] {
84 &self.znx().data
85 }
86
87 /// Returns a pointer to the underlying raw bytes array.
88 fn ptr(&self) -> *mut u8 {
89 self.znx().ptr
90 }
91
92 /// Returns the total number of small polynomials.
93 fn poly_count(&self) -> usize {
94 self.rows() * self.cols() * self.size()
95 }
96}
97
98pub trait ZnxSliceSize {
99 /// Returns the slice size, which is the offset between
100 /// two size of the same column.
101 fn sl(&self) -> usize;
102}
103
104pub trait ZnxAlloc<B: Backend>
105where
106 Self: Sized + ZnxInfos,
107{
108 type Scalar;
109 fn new(module: &Module<B>, rows: usize, cols: usize, size: usize) -> Self {
110 let bytes: Vec<u8> = alloc_aligned::<u8>(Self::bytes_of(module, rows, cols, size));
111 Self::from_bytes(module, rows, cols, size, bytes)
112 }
113
114 fn from_bytes(module: &Module<B>, rows: usize, cols: usize, size: usize, mut bytes: Vec<u8>) -> Self {
115 let mut res: Self = Self::from_bytes_borrow(module, rows, cols, size, &mut bytes);
116 res.znx_mut().data = bytes;
117 res
118 }
119
120 fn from_bytes_borrow(module: &Module<B>, rows: usize, cols: usize, size: usize, bytes: &mut [u8]) -> Self;
121
122 fn bytes_of(module: &Module<B>, rows: usize, cols: usize, size: usize) -> usize;
123}
124
125pub trait ZnxLayout: ZnxInfos {
126 type Scalar;
127
128 /// Returns true if the receiver is only borrowing the data.
129 fn borrowing(&self) -> bool {
130 self.znx().data.len() == 0
131 }
132
133 /// Returns a non-mutable pointer to the underlying coefficients array.
134 fn as_ptr(&self) -> *const Self::Scalar {
135 self.znx().ptr as *const Self::Scalar
136 }
137
138 /// Returns a mutable pointer to the underlying coefficients array.
139 fn as_mut_ptr(&mut self) -> *mut Self::Scalar {
140 self.znx_mut().ptr as *mut Self::Scalar
141 }
142
143 /// Returns a non-mutable reference to the entire underlying coefficient array.
144 fn raw(&self) -> &[Self::Scalar] {
145 unsafe { std::slice::from_raw_parts(self.as_ptr(), self.n() * self.poly_count()) }
146 }
147
148 /// Returns a mutable reference to the entire underlying coefficient array.
149 fn raw_mut(&mut self) -> &mut [Self::Scalar] {
150 unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.n() * self.poly_count()) }
151 }
152
153 /// Returns a non-mutable pointer starting at the j-th small polynomial of the i-th column.
154 fn at_ptr(&self, i: usize, j: usize) -> *const Self::Scalar {
155 #[cfg(debug_assertions)]
156 {
157 assert!(i < self.cols());
158 assert!(j < self.size());
159 }
160 let offset: usize = self.n() * (j * self.cols() + i);
161 unsafe { self.as_ptr().add(offset) }
162 }
163
164 /// Returns a mutable pointer starting at the j-th small polynomial of the i-th column.
165 fn at_mut_ptr(&mut self, i: usize, j: usize) -> *mut Self::Scalar {
166 #[cfg(debug_assertions)]
167 {
168 assert!(i < self.cols());
169 assert!(j < self.size());
170 }
171 let offset: usize = self.n() * (j * self.cols() + i);
172 unsafe { self.as_mut_ptr().add(offset) }
173 }
174
175 /// Returns non-mutable reference to the (i, j)-th small polynomial.
176 fn at(&self, i: usize, j: usize) -> &[Self::Scalar] {
177 unsafe { std::slice::from_raw_parts(self.at_ptr(i, j), self.n()) }
178 }
179
180 /// Returns mutable reference to the (i, j)-th small polynomial.
181 fn at_mut(&mut self, i: usize, j: usize) -> &mut [Self::Scalar] {
182 unsafe { std::slice::from_raw_parts_mut(self.at_mut_ptr(i, j), self.n()) }
183 }
184}
185
186use std::convert::TryFrom;
187use std::num::TryFromIntError;
188use std::ops::{Add, AddAssign, Div, Mul, Neg, Shl, Shr, Sub};
189pub trait IntegerType:
190 Copy
191 + std::fmt::Debug
192 + Default
193 + PartialEq
194 + PartialOrd
195 + Add<Output = Self>
196 + Sub<Output = Self>
197 + Mul<Output = Self>
198 + Div<Output = Self>
199 + Neg<Output = Self>
200 + Shr<Output = Self>
201 + Shl<Output = Self>
202 + AddAssign
203 + TryFrom<usize, Error = TryFromIntError>
204{
205 const BITS: u32;
206}
207
208impl IntegerType for i64 {
209 const BITS: u32 = 64;
210}
211
212impl IntegerType for i128 {
213 const BITS: u32 = 128;
214}
215
216pub trait ZnxZero: ZnxLayout
217where
218 Self: Sized,
219{
220 fn zero(&mut self) {
221 unsafe {
222 std::ptr::write_bytes(
223 self.as_mut_ptr(),
224 0,
225 self.n() * size_of::<Self::Scalar>() * self.poly_count(),
226 );
227 }
228 }
229
230 fn zero_at(&mut self, i: usize, j: usize) {
231 unsafe {
232 std::ptr::write_bytes(
233 self.at_mut_ptr(i, j),
234 0,
235 self.n() * size_of::<Self::Scalar>(),
236 );
237 }
238 }
239}
240
241pub trait ZnxRsh: ZnxLayout + ZnxZero
242where
243 Self: Sized,
244 Self::Scalar: IntegerType,
245{
246 fn rsh(&mut self, k: usize, log_base2k: usize, col: usize, carry: &mut [u8]) {
247 rsh(k, log_base2k, self, col, carry)
248 }
249}
250
251pub fn rsh<V: ZnxRsh + ZnxZero>(k: usize, log_base2k: usize, a: &mut V, a_col: usize, tmp_bytes: &mut [u8])
252where
253 V::Scalar: IntegerType,
254{
255 let n: usize = a.n();
256 let size: usize = a.size();
257 let cols: usize = a.cols();
258
259 #[cfg(debug_assertions)]
260 {
261 assert!(
262 tmp_bytes.len() >= rsh_tmp_bytes::<V::Scalar>(n),
263 "invalid carry: carry.len()/size_ofSelf::Scalar={} < rsh_tmp_bytes({}, {})",
264 tmp_bytes.len() / size_of::<V::Scalar>(),
265 n,
266 size,
267 );
268 assert_alignement(tmp_bytes.as_ptr());
269 }
270
271 let size: usize = a.size();
272 let steps: usize = k / log_base2k;
273
274 a.raw_mut().rotate_right(n * steps * cols);
275 (0..cols).for_each(|i| {
276 (0..steps).for_each(|j| {
277 a.zero_at(i, j);
278 })
279 });
280
281 let k_rem: usize = k % log_base2k;
282
283 if k_rem != 0 {
284 let carry: &mut [V::Scalar] = cast_mut(tmp_bytes);
285
286 unsafe {
287 std::ptr::write_bytes(carry.as_mut_ptr(), 0, n * size_of::<V::Scalar>());
288 }
289
290 let log_base2k_t: V::Scalar = V::Scalar::try_from(log_base2k).unwrap();
291 let shift: V::Scalar = V::Scalar::try_from(V::Scalar::BITS as usize - k_rem).unwrap();
292 let k_rem_t: V::Scalar = V::Scalar::try_from(k_rem).unwrap();
293
294 (steps..size).for_each(|i| {
295 izip!(carry.iter_mut(), a.at_mut(a_col, i).iter_mut()).for_each(|(ci, xi)| {
296 *xi += *ci << log_base2k_t;
297 *ci = get_base_k_carry(*xi, shift);
298 *xi = (*xi - *ci) >> k_rem_t;
299 });
300 })
301 }
302}
303
304#[inline(always)]
305fn get_base_k_carry<T: IntegerType>(x: T, shift: T) -> T {
306 (x << shift) >> shift
307}
308
309pub fn rsh_tmp_bytes<T: IntegerType>(n: usize) -> usize {
310 n * std::mem::size_of::<T>()
311}
312
313pub fn switch_degree<T: ZnxLayout + ZnxZero>(b: &mut T, col_b: usize, a: &T, col_a: usize)
314where
315 <T as ZnxLayout>::Scalar: IntegerType,
316{
317 let (n_in, n_out) = (a.n(), b.n());
318 let (gap_in, gap_out): (usize, usize);
319
320 if n_in > n_out {
321 (gap_in, gap_out) = (n_in / n_out, 1)
322 } else {
323 (gap_in, gap_out) = (1, n_out / n_in);
324 b.zero();
325 }
326
327 let size: usize = min(a.size(), b.size());
328
329 (0..size).for_each(|i| {
330 izip!(
331 a.at(col_a, i).iter().step_by(gap_in),
332 b.at_mut(col_b, i).iter_mut().step_by(gap_out)
333 )
334 .for_each(|(x_in, x_out)| *x_out = *x_in);
335 });
336}