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