arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/vec_znx_ops.rs
base2k/src/vec_znx_ops.rsRaw
  1use crate::ffi::vec_znx;
  2use crate::{
  3    Backend, Module, ScalarZnxToRef, Scratch, VecZnx, VecZnxOwned, VecZnxToMut, VecZnxToRef, ZnxInfos, ZnxSliceSize, ZnxView,
  4    ZnxViewMut, ZnxZero,
  5};
  6use itertools::izip;
  7use std::cmp::min;
  8
  9pub trait VecZnxAlloc {
 10    /// Allocates a new [VecZnx].
 11    ///
 12    /// # Arguments
 13    ///
 14    /// * `cols`: the number of polynomials.
 15    /// * `size`: the number small polynomials per column.
 16    fn new_vec_znx(&self, cols: usize, size: usize) -> VecZnxOwned;
 17
 18    /// Instantiates a new [VecZnx] from a slice of bytes.
 19    /// The returned [VecZnx] takes ownership of the slice of bytes.
 20    ///
 21    /// # Arguments
 22    ///
 23    /// * `cols`: the number of polynomials.
 24    /// * `size`: the number small polynomials per column.
 25    ///
 26    /// # Panic
 27    /// Requires the slice of bytes to be equal to [VecZnxOps::bytes_of_vec_znx].
 28    fn new_vec_znx_from_bytes(&self, cols: usize, size: usize, bytes: Vec<u8>) -> VecZnxOwned;
 29
 30    /// Returns the number of bytes necessary to allocate
 31    /// a new [VecZnx] through [VecZnxOps::new_vec_znx_from_bytes]
 32    /// or [VecZnxOps::new_vec_znx_from_bytes_borrow].
 33    fn bytes_of_vec_znx(&self, cols: usize, size: usize) -> usize;
 34}
 35
 36pub trait VecZnxOps {
 37    /// Normalizes the selected column of `a` and stores the result into the selected column of `res`.
 38    fn vec_znx_normalize<R, A>(&self, log_base2k: usize, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
 39    where
 40        R: VecZnxToMut,
 41        A: VecZnxToRef;
 42
 43    /// Normalizes the selected column of `a`.
 44    fn vec_znx_normalize_inplace<A>(&self, log_base2k: usize, a: &mut A, a_col: usize, scratch: &mut Scratch)
 45    where
 46        A: VecZnxToMut;
 47
 48    /// Adds the selected column of `a` to the selected column of `b` and writes the result on the selected column of `res`.
 49    fn vec_znx_add<R, A, B>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, b: &B, b_col: usize)
 50    where
 51        R: VecZnxToMut,
 52        A: VecZnxToRef,
 53        B: VecZnxToRef;
 54
 55    /// Adds the selected column of `a` to the selected column of `res` and writes the result on the selected column of `res`.
 56    fn vec_znx_add_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
 57    where
 58        R: VecZnxToMut,
 59        A: VecZnxToRef;
 60
 61    /// Adds the selected column of `a` on the selected column and limb of `res`.
 62    fn vec_znx_add_scalar_inplace<R, A>(&self, res: &mut R, res_col: usize, res_limb: usize, a: &A, b_col: usize)
 63    where
 64        R: VecZnxToMut,
 65        A: ScalarZnxToRef;
 66
 67    /// Subtracts the selected column of `b` from the selected column of `a` and writes the result on the selected column of `res`.
 68    fn vec_znx_sub<R, A, B>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, b: &B, b_col: usize)
 69    where
 70        R: VecZnxToMut,
 71        A: VecZnxToRef,
 72        B: VecZnxToRef;
 73
 74    /// Subtracts the selected column of `a` from the selected column of `res` inplace.
 75    ///
 76    /// res[res_col] -= a[a_col]
 77    fn vec_znx_sub_ab_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
 78    where
 79        R: VecZnxToMut,
 80        A: VecZnxToRef;
 81
 82    /// Subtracts the selected column of `res` from the selected column of `a` and inplace mutates `res`
 83    ///
 84    /// res[res_col] = a[a_col] - res[res_col]
 85    fn vec_znx_sub_ba_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
 86    where
 87        R: VecZnxToMut,
 88        A: VecZnxToRef;
 89
 90    /// Subtracts the selected column of `a` on the selected column and limb of `res`.
 91    fn vec_znx_sub_scalar_inplace<R, A>(&self, res: &mut R, res_col: usize, res_limb: usize, a: &A, b_col: usize)
 92    where
 93        R: VecZnxToMut,
 94        A: ScalarZnxToRef;
 95
 96    // Negates the selected column of `a` and stores the result in `res_col` of `res`.
 97    fn vec_znx_negate<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
 98    where
 99        R: VecZnxToMut,
100        A: VecZnxToRef;
101
102    /// Negates the selected column of `a`.
103    fn vec_znx_negate_inplace<A>(&self, a: &mut A, a_col: usize)
104    where
105        A: VecZnxToMut;
106
107    /// Multiplies the selected column of `a` by X^k and stores the result in `res_col` of `res`.
108    fn vec_znx_rotate<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
109    where
110        R: VecZnxToMut,
111        A: VecZnxToRef;
112
113    /// Multiplies the selected column of `a` by X^k.
114    fn vec_znx_rotate_inplace<A>(&self, k: i64, a: &mut A, a_col: usize)
115    where
116        A: VecZnxToMut;
117
118    /// Applies the automorphism X^i -> X^ik on the selected column of `a` and stores the result in `res_col` column of `res`.
119    fn vec_znx_automorphism<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
120    where
121        R: VecZnxToMut,
122        A: VecZnxToRef;
123
124    /// Applies the automorphism X^i -> X^ik on the selected column of `a`.
125    fn vec_znx_automorphism_inplace<A>(&self, k: i64, a: &mut A, a_col: usize)
126    where
127        A: VecZnxToMut;
128
129    /// Splits the selected columns of `b` into subrings and copies them them into the selected column of `res`.
130    ///
131    /// # Panics
132    ///
133    /// This method requires that all [VecZnx] of b have the same ring degree
134    /// and that b.n() * b.len() <= a.n()
135    fn vec_znx_split<R, A>(&self, res: &mut Vec<R>, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
136    where
137        R: VecZnxToMut,
138        A: VecZnxToRef;
139
140    /// Merges the subrings of the selected column of `a` into the selected column of `res`.
141    ///
142    /// # Panics
143    ///
144    /// This method requires that all [VecZnx] of a have the same ring degree
145    /// and that a.n() * a.len() <= b.n()
146    fn vec_znx_merge<R, A>(&self, res: &mut R, res_col: usize, a: Vec<A>, a_col: usize)
147    where
148        R: VecZnxToMut,
149        A: VecZnxToRef;
150
151    fn switch_degree<R, A>(&self, r: &mut R, col_b: usize, a: &A, col_a: usize)
152    where
153        R: VecZnxToMut,
154        A: VecZnxToRef;
155}
156
157pub trait VecZnxScratch {
158    /// Returns the minimum number of bytes necessary for normalization.
159    fn vec_znx_normalize_tmp_bytes(&self) -> usize;
160}
161
162impl<B: Backend> VecZnxAlloc for Module<B> {
163    fn new_vec_znx(&self, cols: usize, size: usize) -> VecZnxOwned {
164        VecZnxOwned::new::<i64>(self.n(), cols, size)
165    }
166
167    fn bytes_of_vec_znx(&self, cols: usize, size: usize) -> usize {
168        VecZnxOwned::bytes_of::<i64>(self.n(), cols, size)
169    }
170
171    fn new_vec_znx_from_bytes(&self, cols: usize, size: usize, bytes: Vec<u8>) -> VecZnxOwned {
172        VecZnxOwned::new_from_bytes::<i64>(self.n(), cols, size, bytes)
173    }
174}
175
176impl<BACKEND: Backend> VecZnxOps for Module<BACKEND> {
177    fn vec_znx_normalize<R, A>(&self, log_base2k: usize, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
178    where
179        R: VecZnxToMut,
180        A: VecZnxToRef,
181    {
182        let a: VecZnx<&[u8]> = a.to_ref();
183        let mut res: VecZnx<&mut [u8]> = res.to_mut();
184
185        #[cfg(debug_assertions)]
186        {
187            assert_eq!(a.n(), self.n());
188            assert_eq!(res.n(), self.n());
189        }
190
191        let (tmp_bytes, _) = scratch.tmp_slice(self.vec_znx_normalize_tmp_bytes());
192
193        unsafe {
194            vec_znx::vec_znx_normalize_base2k(
195                self.ptr,
196                log_base2k as u64,
197                res.at_mut_ptr(res_col, 0),
198                res.size() as u64,
199                res.sl() as u64,
200                a.at_ptr(a_col, 0),
201                a.size() as u64,
202                a.sl() as u64,
203                tmp_bytes.as_mut_ptr(),
204            );
205        }
206    }
207
208    fn vec_znx_normalize_inplace<A>(&self, log_base2k: usize, a: &mut A, a_col: usize, scratch: &mut Scratch)
209    where
210        A: VecZnxToMut,
211    {
212        let mut a: VecZnx<&mut [u8]> = a.to_mut();
213
214        #[cfg(debug_assertions)]
215        {
216            assert_eq!(a.n(), self.n());
217        }
218
219        let (tmp_bytes, _) = scratch.tmp_slice(self.vec_znx_normalize_tmp_bytes());
220
221        unsafe {
222            vec_znx::vec_znx_normalize_base2k(
223                self.ptr,
224                log_base2k as u64,
225                a.at_mut_ptr(a_col, 0),
226                a.size() as u64,
227                a.sl() as u64,
228                a.at_ptr(a_col, 0),
229                a.size() as u64,
230                a.sl() as u64,
231                tmp_bytes.as_mut_ptr(),
232            );
233        }
234    }
235
236    fn vec_znx_add<R, A, B>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, b: &B, b_col: usize)
237    where
238        R: VecZnxToMut,
239        A: VecZnxToRef,
240        B: VecZnxToRef,
241    {
242        let a: VecZnx<&[u8]> = a.to_ref();
243        let b: VecZnx<&[u8]> = b.to_ref();
244        let mut res: VecZnx<&mut [u8]> = res.to_mut();
245
246        #[cfg(debug_assertions)]
247        {
248            assert_eq!(a.n(), self.n());
249            assert_eq!(b.n(), self.n());
250            assert_eq!(res.n(), self.n());
251            assert_ne!(a.as_ptr(), b.as_ptr());
252        }
253        unsafe {
254            vec_znx::vec_znx_add(
255                self.ptr,
256                res.at_mut_ptr(res_col, 0),
257                res.size() as u64,
258                res.sl() as u64,
259                a.at_ptr(a_col, 0),
260                a.size() as u64,
261                a.sl() as u64,
262                b.at_ptr(b_col, 0),
263                b.size() as u64,
264                b.sl() as u64,
265            )
266        }
267    }
268
269    fn vec_znx_add_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
270    where
271        R: VecZnxToMut,
272        A: VecZnxToRef,
273    {
274        let a: VecZnx<&[u8]> = a.to_ref();
275        let mut res: VecZnx<&mut [u8]> = res.to_mut();
276
277        #[cfg(debug_assertions)]
278        {
279            assert_eq!(a.n(), self.n());
280            assert_eq!(res.n(), self.n());
281        }
282        unsafe {
283            vec_znx::vec_znx_add(
284                self.ptr,
285                res.at_mut_ptr(res_col, 0),
286                res.size() as u64,
287                res.sl() as u64,
288                a.at_ptr(a_col, 0),
289                a.size() as u64,
290                a.sl() as u64,
291                res.at_ptr(res_col, 0),
292                res.size() as u64,
293                res.sl() as u64,
294            )
295        }
296    }
297
298    fn vec_znx_add_scalar_inplace<R, A>(&self, res: &mut R, res_col: usize, res_limb: usize, a: &A, a_col: usize)
299    where
300        R: VecZnxToMut,
301        A: ScalarZnxToRef,
302    {
303        let mut res: VecZnx<&mut [u8]> = res.to_mut();
304        let a: crate::ScalarZnx<&[u8]> = a.to_ref();
305
306        #[cfg(debug_assertions)]
307        {
308            assert_eq!(a.n(), self.n());
309            assert_eq!(res.n(), self.n());
310        }
311
312        unsafe {
313            vec_znx::vec_znx_add(
314                self.ptr,
315                res.at_mut_ptr(res_col, res_limb),
316                1 as u64,
317                res.sl() as u64,
318                a.at_ptr(a_col, 0),
319                a.size() as u64,
320                a.sl() as u64,
321                res.at_ptr(res_col, res_limb),
322                1 as u64,
323                res.sl() as u64,
324            )
325        }
326    }
327
328    fn vec_znx_sub<R, A, B>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, b: &B, b_col: usize)
329    where
330        R: VecZnxToMut,
331        A: VecZnxToRef,
332        B: VecZnxToRef,
333    {
334        let a: VecZnx<&[u8]> = a.to_ref();
335        let b: VecZnx<&[u8]> = b.to_ref();
336        let mut res: VecZnx<&mut [u8]> = res.to_mut();
337
338        #[cfg(debug_assertions)]
339        {
340            assert_eq!(a.n(), self.n());
341            assert_eq!(b.n(), self.n());
342            assert_eq!(res.n(), self.n());
343            assert_ne!(a.as_ptr(), b.as_ptr());
344        }
345        unsafe {
346            vec_znx::vec_znx_sub(
347                self.ptr,
348                res.at_mut_ptr(res_col, 0),
349                res.size() as u64,
350                res.sl() as u64,
351                a.at_ptr(a_col, 0),
352                a.size() as u64,
353                a.sl() as u64,
354                b.at_ptr(b_col, 0),
355                b.size() as u64,
356                b.sl() as u64,
357            )
358        }
359    }
360
361    fn vec_znx_sub_scalar_inplace<R, A>(&self, res: &mut R, res_col: usize, res_limb: usize, a: &A, a_col: usize)
362    where
363        R: VecZnxToMut,
364        A: ScalarZnxToRef,
365    {
366        let mut res: VecZnx<&mut [u8]> = res.to_mut();
367        let a: crate::ScalarZnx<&[u8]> = a.to_ref();
368
369        #[cfg(debug_assertions)]
370        {
371            assert_eq!(a.n(), self.n());
372            assert_eq!(res.n(), self.n());
373        }
374
375        unsafe {
376            vec_znx::vec_znx_sub(
377                self.ptr,
378                res.at_mut_ptr(res_col, res_limb),
379                1 as u64,
380                res.sl() as u64,
381                a.at_ptr(a_col, 0),
382                a.size() as u64,
383                a.sl() as u64,
384                res.at_ptr(res_col, res_limb),
385                1 as u64,
386                res.sl() as u64,
387            )
388        }
389    }
390
391    fn vec_znx_sub_ab_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
392    where
393        R: VecZnxToMut,
394        A: VecZnxToRef,
395    {
396        let a: VecZnx<&[u8]> = a.to_ref();
397        let mut res: VecZnx<&mut [u8]> = res.to_mut();
398        #[cfg(debug_assertions)]
399        {
400            assert_eq!(a.n(), self.n());
401            assert_eq!(res.n(), self.n());
402        }
403        unsafe {
404            vec_znx::vec_znx_sub(
405                self.ptr,
406                res.at_mut_ptr(res_col, 0),
407                res.size() as u64,
408                res.sl() as u64,
409                res.at_ptr(res_col, 0),
410                res.size() as u64,
411                res.sl() as u64,
412                a.at_ptr(a_col, 0),
413                a.size() as u64,
414                a.sl() as u64,
415            )
416        }
417    }
418
419    fn vec_znx_sub_ba_inplace<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
420    where
421        R: VecZnxToMut,
422        A: VecZnxToRef,
423    {
424        let a: VecZnx<&[u8]> = a.to_ref();
425        let mut res: VecZnx<&mut [u8]> = res.to_mut();
426        #[cfg(debug_assertions)]
427        {
428            assert_eq!(a.n(), self.n());
429            assert_eq!(res.n(), self.n());
430        }
431        unsafe {
432            vec_znx::vec_znx_sub(
433                self.ptr,
434                res.at_mut_ptr(res_col, 0),
435                res.size() as u64,
436                res.sl() as u64,
437                a.at_ptr(a_col, 0),
438                a.size() as u64,
439                a.sl() as u64,
440                res.at_ptr(res_col, 0),
441                res.size() as u64,
442                res.sl() as u64,
443            )
444        }
445    }
446
447    fn vec_znx_negate<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
448    where
449        R: VecZnxToMut,
450        A: VecZnxToRef,
451    {
452        let a: VecZnx<&[u8]> = a.to_ref();
453        let mut res: VecZnx<&mut [u8]> = res.to_mut();
454        #[cfg(debug_assertions)]
455        {
456            assert_eq!(a.n(), self.n());
457            assert_eq!(res.n(), self.n());
458        }
459        unsafe {
460            vec_znx::vec_znx_negate(
461                self.ptr,
462                res.at_mut_ptr(res_col, 0),
463                res.size() as u64,
464                res.sl() as u64,
465                a.at_ptr(a_col, 0),
466                a.size() as u64,
467                a.sl() as u64,
468            )
469        }
470    }
471
472    fn vec_znx_negate_inplace<A>(&self, a: &mut A, a_col: usize)
473    where
474        A: VecZnxToMut,
475    {
476        let mut a: VecZnx<&mut [u8]> = a.to_mut();
477        #[cfg(debug_assertions)]
478        {
479            assert_eq!(a.n(), self.n());
480        }
481        unsafe {
482            vec_znx::vec_znx_negate(
483                self.ptr,
484                a.at_mut_ptr(a_col, 0),
485                a.size() as u64,
486                a.sl() as u64,
487                a.at_ptr(a_col, 0),
488                a.size() as u64,
489                a.sl() as u64,
490            )
491        }
492    }
493
494    fn vec_znx_rotate<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
495    where
496        R: VecZnxToMut,
497        A: VecZnxToRef,
498    {
499        let a: VecZnx<&[u8]> = a.to_ref();
500        let mut res: VecZnx<&mut [u8]> = res.to_mut();
501        #[cfg(debug_assertions)]
502        {
503            assert_eq!(a.n(), self.n());
504            assert_eq!(res.n(), self.n());
505        }
506        unsafe {
507            vec_znx::vec_znx_rotate(
508                self.ptr,
509                k,
510                res.at_mut_ptr(res_col, 0),
511                res.size() as u64,
512                res.sl() as u64,
513                a.at_ptr(a_col, 0),
514                a.size() as u64,
515                a.sl() as u64,
516            )
517        }
518    }
519
520    fn vec_znx_rotate_inplace<A>(&self, k: i64, a: &mut A, a_col: usize)
521    where
522        A: VecZnxToMut,
523    {
524        let mut a: VecZnx<&mut [u8]> = a.to_mut();
525        #[cfg(debug_assertions)]
526        {
527            assert_eq!(a.n(), self.n());
528        }
529        unsafe {
530            vec_znx::vec_znx_rotate(
531                self.ptr,
532                k,
533                a.at_mut_ptr(a_col, 0),
534                a.size() as u64,
535                a.sl() as u64,
536                a.at_ptr(a_col, 0),
537                a.size() as u64,
538                a.sl() as u64,
539            )
540        }
541    }
542
543    fn vec_znx_automorphism<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
544    where
545        R: VecZnxToMut,
546        A: VecZnxToRef,
547    {
548        let a: VecZnx<&[u8]> = a.to_ref();
549        let mut res: VecZnx<&mut [u8]> = res.to_mut();
550        #[cfg(debug_assertions)]
551        {
552            assert_eq!(a.n(), self.n());
553            assert_eq!(res.n(), self.n());
554        }
555        unsafe {
556            vec_znx::vec_znx_automorphism(
557                self.ptr,
558                k,
559                res.at_mut_ptr(res_col, 0),
560                res.size() as u64,
561                res.sl() as u64,
562                a.at_ptr(a_col, 0),
563                a.size() as u64,
564                a.sl() as u64,
565            )
566        }
567    }
568
569    fn vec_znx_automorphism_inplace<A>(&self, k: i64, a: &mut A, a_col: usize)
570    where
571        A: VecZnxToMut,
572    {
573        let mut a: VecZnx<&mut [u8]> = a.to_mut();
574        #[cfg(debug_assertions)]
575        {
576            assert_eq!(a.n(), self.n());
577        }
578        unsafe {
579            vec_znx::vec_znx_automorphism(
580                self.ptr,
581                k,
582                a.at_mut_ptr(a_col, 0),
583                a.size() as u64,
584                a.sl() as u64,
585                a.at_ptr(a_col, 0),
586                a.size() as u64,
587                a.sl() as u64,
588            )
589        }
590    }
591
592    fn vec_znx_split<R, A>(&self, res: &mut Vec<R>, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
593    where
594        R: VecZnxToMut,
595        A: VecZnxToRef,
596    {
597        let a: VecZnx<&[u8]> = a.to_ref();
598
599        let (n_in, n_out) = (a.n(), res[0].to_mut().n());
600
601        let (mut buf, _) = scratch.tmp_vec_znx(self, 1, a.size());
602
603        debug_assert!(
604            n_out < n_in,
605            "invalid a: output ring degree should be smaller"
606        );
607        res[1..].iter_mut().for_each(|bi| {
608            debug_assert_eq!(
609                bi.to_mut().n(),
610                n_out,
611                "invalid input a: all VecZnx must have the same degree"
612            )
613        });
614
615        res.iter_mut().enumerate().for_each(|(i, bi)| {
616            if i == 0 {
617                self.switch_degree(bi, res_col, &a, a_col);
618                self.vec_znx_rotate(-1, &mut buf, 0, &a, a_col);
619            } else {
620                self.switch_degree(bi, res_col, &mut buf, a_col);
621                self.vec_znx_rotate_inplace(-1, &mut buf, a_col);
622            }
623        })
624    }
625
626    fn vec_znx_merge<R, A>(&self, res: &mut R, res_col: usize, a: Vec<A>, a_col: usize)
627    where
628        R: VecZnxToMut,
629        A: VecZnxToRef,
630    {
631        let mut res: VecZnx<&mut [u8]> = res.to_mut();
632
633        let (n_in, n_out) = (res.n(), a[0].to_ref().n());
634
635        debug_assert!(
636            n_out < n_in,
637            "invalid a: output ring degree should be smaller"
638        );
639        a[1..].iter().for_each(|ai| {
640            debug_assert_eq!(
641                ai.to_ref().n(),
642                n_out,
643                "invalid input a: all VecZnx must have the same degree"
644            )
645        });
646
647        a.iter().enumerate().for_each(|(_, ai)| {
648            self.switch_degree(&mut res, res_col, ai, a_col);
649            self.vec_znx_rotate_inplace(-1, &mut res, res_col);
650        });
651
652        self.vec_znx_rotate_inplace(a.len() as i64, &mut res, res_col);
653    }
654
655    fn switch_degree<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize)
656    where
657        R: VecZnxToMut,
658        A: VecZnxToRef,
659    {
660        let a: VecZnx<&[u8]> = a.to_ref();
661        let mut res: VecZnx<&mut [u8]> = res.to_mut();
662
663        let (n_in, n_out) = (a.n(), res.n());
664        let (gap_in, gap_out): (usize, usize);
665
666        if n_in > n_out {
667            (gap_in, gap_out) = (n_in / n_out, 1)
668        } else {
669            (gap_in, gap_out) = (1, n_out / n_in);
670            res.zero();
671        }
672
673        let size: usize = min(a.size(), res.size());
674
675        (0..size).for_each(|i| {
676            izip!(
677                a.at(a_col, i).iter().step_by(gap_in),
678                res.at_mut(res_col, i).iter_mut().step_by(gap_out)
679            )
680            .for_each(|(x_in, x_out)| *x_out = *x_in);
681        });
682    }
683}
684
685impl<B: Backend> VecZnxScratch for Module<B> {
686    fn vec_znx_normalize_tmp_bytes(&self) -> usize {
687        unsafe { vec_znx::vec_znx_normalize_base2k_tmp_bytes(self.ptr) as usize }
688    }
689}