arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/vec_znx.rs
base2k/src/vec_znx.rsRaw
  1use crate::cast_mut;
  2use crate::ffi::vec_znx;
  3use crate::ffi::znx;
  4use crate::ffi::znx::znx_zero_i64_ref;
  5use crate::{alias_mut_slice_to_vec, alloc_aligned};
  6use crate::{Infos, Module};
  7use itertools::izip;
  8use std::cmp::min;
  9
 10pub trait VecZnxApi {
 11    type Owned: VecZnxApi + Infos;
 12
 13    fn from_bytes(n: usize, limbs: usize, bytes: &mut [u8]) -> Self::Owned;
 14
 15    /// Returns the minimum size of the [u8] array required to assign a
 16    /// new backend array.
 17    fn bytes_of(n: usize, limbs: usize) -> usize;
 18
 19    /// Returns the backing array.
 20    fn raw(&self) -> &[i64];
 21
 22    /// Returns the mutable backing array.
 23    fn raw_mut(&mut self) -> &mut [i64];
 24
 25    /// Returns a non-mutable pointer to the backing array.
 26    fn as_ptr(&self) -> *const i64;
 27
 28    /// Returns a mutable pointer to the backing array.
 29    fn as_mut_ptr(&mut self) -> *mut i64;
 30
 31    /// Returns a non-mutable reference to the i-th limb.
 32    fn at(&self, i: usize) -> &[i64];
 33
 34    /// Returns a mutable reference to the i-th limb .
 35    fn at_mut(&mut self, i: usize) -> &mut [i64];
 36
 37    /// Returns a non-mutable pointer to the i-th limb.
 38    fn at_ptr(&self, i: usize) -> *const i64;
 39
 40    /// Returns a mutable pointer to the i-th limb.
 41    fn at_mut_ptr(&mut self, i: usize) -> *mut i64;
 42
 43    /// Zeroes the backing array.
 44    fn zero(&mut self);
 45    fn normalize(&mut self, log_base2k: usize, carry: &mut [u8]);
 46
 47    /// Right shifts the coefficients by k bits.
 48    ///
 49    /// # Arguments
 50    ///
 51    /// * `log_base2k`: the base two logarithm of the coefficients decomposition.
 52    /// * `k`: the shift amount.
 53    /// * `carry`: scratch space of size at least equal to self.n() * self.limbs() << 3.
 54    ///
 55    /// # Panics
 56    ///
 57    /// The method will panic if carry.len() < self.n() * self.limbs() << 3.
 58    fn rsh(&mut self, log_base2k: usize, k: usize, carry: &mut [u8]);
 59
 60    /// If self.n() > a.n(): Extracts X^{i*self.n()/a.n()} -> X^{i}.
 61    /// If self.n() < a.n(): Extracts X^{i} -> X^{i*a.n()/self.n()}.
 62    ///
 63    /// # Arguments
 64    ///
 65    /// * `a`: the receiver polynomial in which the extracted coefficients are stored.
 66    fn switch_degree<T: VecZnxApi + Infos>(&self, a: &mut T)
 67    where
 68        Self: AsRef<T>;
 69
 70    fn print(&self, limbs: usize, n: usize);
 71}
 72
 73pub fn bytes_of_vec_znx(n: usize, limbs: usize) -> usize {
 74    n * limbs * 8
 75}
 76
 77pub struct VecZnxBorrow {
 78    pub n: usize,
 79    pub limbs: usize,
 80    pub data: *mut i64,
 81}
 82
 83impl VecZnxApi for VecZnxBorrow {
 84    type Owned = VecZnxBorrow;
 85
 86    /// Returns a new struct implementing [VecZnxBorrow] with the provided data as backing array.
 87    ///
 88    /// The struct will *NOT* take ownership of buf[..[VecZnx::bytes_of]]
 89    ///
 90    /// User must ensure that data is properly alligned and that
 91    /// the size of data is at least equal to [VecZnx::bytes_of].
 92    fn from_bytes(n: usize, limbs: usize, bytes: &mut [u8]) -> Self::Owned {
 93        let size = Self::bytes_of(n, limbs);
 94        assert!(
 95            bytes.len() >= size,
 96            "invalid buffer: buf.len()={} < self.buffer_size(n={}, limbs={})={}",
 97            bytes.len(),
 98            n,
 99            limbs,
100            size
101        );
102        VecZnxBorrow {
103            n: n,
104            limbs: limbs,
105            data: cast_mut(&mut bytes[..size]).as_mut_ptr(),
106        }
107    }
108
109    fn bytes_of(n: usize, limbs: usize) -> usize {
110        bytes_of_vec_znx(n, limbs)
111    }
112
113    fn as_ptr(&self) -> *const i64 {
114        self.data
115    }
116
117    fn as_mut_ptr(&mut self) -> *mut i64 {
118        self.data
119    }
120
121    fn raw(&self) -> &[i64] {
122        unsafe { std::slice::from_raw_parts(self.data, self.n * self.limbs) }
123    }
124
125    fn raw_mut(&mut self) -> &mut [i64] {
126        unsafe { std::slice::from_raw_parts_mut(self.data, self.n * self.limbs) }
127    }
128
129    fn at(&self, i: usize) -> &[i64] {
130        let n: usize = self.n();
131        &self.raw()[n * i..n * (i + 1)]
132    }
133
134    fn at_mut(&mut self, i: usize) -> &mut [i64] {
135        let n: usize = self.n();
136        &mut self.raw_mut()[n * i..n * (i + 1)]
137    }
138
139    fn at_ptr(&self, i: usize) -> *const i64 {
140        self.data.wrapping_add(self.n * i)
141    }
142
143    fn at_mut_ptr(&mut self, i: usize) -> *mut i64 {
144        self.data.wrapping_add(self.n * i)
145    }
146
147    fn zero(&mut self) {
148        unsafe {
149            znx_zero_i64_ref((self.n * self.limbs) as u64, self.data);
150        }
151    }
152
153    fn normalize(&mut self, log_base2k: usize, carry: &mut [u8]) {
154        normalize(log_base2k, self, carry)
155    }
156
157    fn rsh(&mut self, log_base2k: usize, k: usize, carry: &mut [u8]) {
158        rsh(log_base2k, self, k, carry)
159    }
160
161    fn switch_degree<T: VecZnxApi + Infos>(&self, a: &mut T)
162    where
163        Self: AsRef<T>,
164    {
165        switch_degree(a, self.as_ref());
166    }
167
168    fn print(&self, limbs: usize, n: usize) {
169        (0..limbs).for_each(|i| println!("{}: {:?}", i, &self.at(i)[..n]))
170    }
171}
172
173impl VecZnxApi for VecZnx {
174    type Owned = VecZnx;
175
176    /// Returns a new struct implementing [VecZnx] with the provided data as backing array.
177    ///
178    /// The struct will take ownership of buf[..[VecZnx::bytes_of]]
179    ///
180    /// User must ensure that data is properly alligned and that
181    /// the size of data is at least equal to [VecZnx::bytes_of].
182    fn from_bytes(n: usize, limbs: usize, buf: &mut [u8]) -> Self::Owned {
183        let size = Self::bytes_of(n, limbs);
184        assert!(
185            buf.len() >= size,
186            "invalid buffer: buf.len()={} < self.buffer_size(n={}, limbs={})={}",
187            buf.len(),
188            n,
189            limbs,
190            size
191        );
192
193        VecZnx {
194            n: n,
195            data: alias_mut_slice_to_vec(cast_mut(&mut buf[..size])),
196        }
197    }
198
199    fn bytes_of(n: usize, limbs: usize) -> usize {
200        bytes_of_vec_znx(n, limbs)
201    }
202
203    fn raw(&self) -> &[i64] {
204        &self.data
205    }
206
207    fn raw_mut(&mut self) -> &mut [i64] {
208        &mut self.data
209    }
210
211    fn as_ptr(&self) -> *const i64 {
212        self.data.as_ptr()
213    }
214
215    fn as_mut_ptr(&mut self) -> *mut i64 {
216        self.data.as_mut_ptr()
217    }
218
219    fn at(&self, i: usize) -> &[i64] {
220        let n: usize = self.n();
221        &self.raw()[n * i..n * (i + 1)]
222    }
223
224    fn at_mut(&mut self, i: usize) -> &mut [i64] {
225        let n: usize = self.n();
226        &mut self.raw_mut()[n * i..n * (i + 1)]
227    }
228
229    fn at_ptr(&self, i: usize) -> *const i64 {
230        &self.data[i * self.n] as *const i64
231    }
232
233    fn at_mut_ptr(&mut self, i: usize) -> *mut i64 {
234        &mut self.data[i * self.n] as *mut i64
235    }
236
237    fn zero(&mut self) {
238        unsafe { znx::znx_zero_i64_ref(self.data.len() as u64, self.data.as_mut_ptr()) }
239    }
240
241    fn normalize(&mut self, log_base2k: usize, carry: &mut [u8]) {
242        normalize(log_base2k, self, carry)
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    fn switch_degree<T: VecZnxApi + Infos>(&self, a: &mut T)
250    where
251        Self: AsRef<T>,
252    {
253        switch_degree(a, self.as_ref())
254    }
255
256    fn print(&self, limbs: usize, n: usize) {
257        (0..limbs).for_each(|i| println!("{}: {:?}", i, &self.at(i)[..n]))
258    }
259}
260
261/// [VecZnx] represents a vector of small norm polynomials of Zn\[X\] with [i64] coefficients.
262/// A [VecZnx] is composed of multiple Zn\[X\] polynomials stored in a single contiguous array
263/// in the memory.
264#[derive(Clone)]
265pub struct VecZnx {
266    /// Polynomial degree.
267    pub n: usize,
268    /// Polynomial coefficients, as a contiguous array. Each limb is equally spaced by n.
269    pub data: Vec<i64>,
270}
271
272impl VecZnx {
273    /// Allocates a new [VecZnx] composed of #limbs polynomials of Z\[X\].
274    pub fn new(n: usize, limbs: usize) -> Self {
275        Self {
276            n: n,
277            data: alloc_aligned::<i64>(n * limbs, 64),
278        }
279    }
280
281    /// Copies the coefficients of `a` on the receiver.
282    /// Copy is done with the minimum size matching both backing arrays.
283    pub fn copy_from(&mut self, a: &VecZnx) {
284        let size = min(self.data.len(), a.data.len());
285        self.data[..size].copy_from_slice(&a.data[..size])
286    }
287
288    /// Truncates the precision of the [VecZnx] by k bits.
289    ///
290    /// # Arguments
291    ///
292    /// * `log_base2k`: the base two logarithm of the coefficients decomposition.
293    /// * `k`: the number of bits of precision to drop.
294    pub fn trunc_pow2(&mut self, log_base2k: usize, k: usize) {
295        if k == 0 {
296            return;
297        }
298
299        self.data
300            .truncate((self.cols() - k / log_base2k) * self.n());
301
302        let k_rem: usize = k % log_base2k;
303
304        if k_rem != 0 {
305            let mask: i64 = ((1 << (log_base2k - k_rem - 1)) - 1) << k_rem;
306            self.at_mut(self.cols() - 1)
307                .iter_mut()
308                .for_each(|x: &mut i64| *x &= mask)
309        }
310    }
311}
312
313pub fn switch_degree<T: VecZnxApi + Infos>(b: &mut T, a: &T) {
314    let (n_in, n_out) = (a.n(), b.n());
315    let (gap_in, gap_out): (usize, usize);
316
317    if n_in > n_out {
318        (gap_in, gap_out) = (n_in / n_out, 1)
319    } else {
320        (gap_in, gap_out) = (1, n_out / n_in);
321        b.zero();
322    }
323
324    let limbs = min(a.cols(), b.cols());
325
326    (0..limbs).for_each(|i| {
327        izip!(
328            a.at(i).iter().step_by(gap_in),
329            b.at_mut(i).iter_mut().step_by(gap_out)
330        )
331        .for_each(|(x_in, x_out)| *x_out = *x_in);
332    });
333}
334
335fn normalize<T: VecZnxApi + Infos>(log_base2k: usize, a: &mut T, carry: &mut [u8]) {
336    let n: usize = a.n();
337
338    assert!(
339        carry.len() >= n * 8,
340        "invalid carry: carry.len()={} < self.n()={}",
341        carry.len(),
342        n
343    );
344
345    let carry_i64: &mut [i64] = cast_mut(carry);
346
347    unsafe {
348        znx::znx_zero_i64_ref(n as u64, carry_i64.as_mut_ptr());
349        (0..a.cols()).rev().for_each(|i| {
350            znx::znx_normalize(
351                n as u64,
352                log_base2k as u64,
353                a.at_mut_ptr(i),
354                carry_i64.as_mut_ptr(),
355                a.at_mut_ptr(i),
356                carry_i64.as_mut_ptr(),
357            )
358        });
359    }
360}
361
362pub fn rsh<T: VecZnxApi + Infos>(log_base2k: usize, a: &mut T, k: usize, carry: &mut [u8]) {
363    let n: usize = a.n();
364
365    assert!(
366        carry.len() >> 3 >= n,
367        "invalid carry: carry.len()/8={} < self.n()={}",
368        carry.len() >> 3,
369        n
370    );
371
372    let limbs: usize = a.cols();
373    let limbs_steps: usize = k / log_base2k;
374
375    a.raw_mut().rotate_right(n * limbs_steps);
376    unsafe {
377        znx::znx_zero_i64_ref((n * limbs_steps) as u64, a.as_mut_ptr());
378    }
379
380    let k_rem = k % log_base2k;
381
382    if k_rem != 0 {
383        let carry_i64: &mut [i64] = cast_mut(carry);
384
385        unsafe {
386            znx::znx_zero_i64_ref(n as u64, carry_i64.as_mut_ptr());
387        }
388
389        let mask: i64 = (1 << k_rem) - 1;
390        let log_base2k: usize = log_base2k;
391
392        (limbs_steps..limbs).for_each(|i| {
393            izip!(carry_i64.iter_mut(), a.at_mut(i).iter_mut()).for_each(|(ci, xi)| {
394                *xi += *ci << log_base2k;
395                *ci = *xi & mask;
396                *xi /= 1 << k_rem;
397            });
398        })
399    }
400}
401
402pub trait VecZnxOps {
403    /// Allocates a new [VecZnx].
404    ///
405    /// # Arguments
406    ///
407    /// * `limbs`: the number of limbs.
408    fn new_vec_znx(&self, limbs: usize) -> VecZnx;
409
410    /// Returns the minimum number of bytes necessary to allocate
411    /// a new [VecZnx] through [VecZnx::from_bytes].
412    fn bytes_of_vec_znx(&self, limbs: usize) -> usize;
413
414    /// c <- a + b.
415    fn vec_znx_add<T: VecZnxApi + Infos>(&self, c: &mut T, a: &T, b: &T);
416
417    /// b <- b + a.
418    fn vec_znx_add_inplace<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T);
419
420    /// c <- a - b.
421    fn vec_znx_sub<T: VecZnxApi + Infos>(&self, c: &mut T, a: &T, b: &T);
422
423    /// b <- b - a.
424    fn vec_znx_sub_inplace<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T);
425
426    /// b <- -a.
427    fn vec_znx_negate<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T);
428
429    /// b <- -b.
430    fn vec_znx_negate_inplace<T: VecZnxApi + Infos>(&self, a: &mut T);
431
432    /// b <- a * X^k (mod X^{n} + 1)
433    fn vec_znx_rotate<T: VecZnxApi + Infos>(&self, k: i64, b: &mut T, a: &T);
434
435    /// a <- a * X^k (mod X^{n} + 1)
436    fn vec_znx_rotate_inplace<T: VecZnxApi + Infos>(&self, k: i64, a: &mut T);
437
438    /// b <- phi_k(a) where phi_k: X^i -> X^{i*k} (mod (X^{n} + 1))
439    fn vec_znx_automorphism<T: VecZnxApi + Infos>(&self, k: i64, b: &mut T, a: &T, a_limbs: usize);
440
441    /// a <- phi_k(a) where phi_k: X^i -> X^{i*k} (mod (X^{n} + 1))
442    fn vec_znx_automorphism_inplace<T: VecZnxApi + Infos>(&self, k: i64, a: &mut T, a_limbs: usize);
443
444    /// Splits b into subrings and copies them them into a.
445    ///
446    /// # Panics
447    ///
448    /// This method requires that all [VecZnx] of b have the same ring degree
449    /// and that b.n() * b.len() <= a.n()
450    fn vec_znx_split<T: VecZnxApi + Infos>(&self, b: &mut Vec<T>, a: &T, buf: &mut T);
451
452    /// Merges the subrings a into b.
453    ///
454    /// # Panics
455    ///
456    /// This method requires that all [VecZnx] of a have the same ring degree
457    /// and that a.n() * a.len() <= b.n()
458    fn vec_znx_merge<T: VecZnxApi + Infos>(&self, b: &mut T, a: &Vec<T>);
459}
460
461impl VecZnxOps for Module {
462    fn new_vec_znx(&self, limbs: usize) -> VecZnx {
463        VecZnx::new(self.n(), limbs)
464    }
465
466    fn bytes_of_vec_znx(&self, limbs: usize) -> usize {
467        self.n() * limbs * 8
468    }
469
470    // c <- a + b
471    fn vec_znx_add<T: VecZnxApi + Infos>(&self, c: &mut T, a: &T, b: &T) {
472        unsafe {
473            vec_znx::vec_znx_add(
474                self.0,
475                c.as_mut_ptr(),
476                c.cols() as u64,
477                c.n() as u64,
478                a.as_ptr(),
479                a.cols() as u64,
480                a.n() as u64,
481                b.as_ptr(),
482                b.cols() as u64,
483                b.n() as u64,
484            )
485        }
486    }
487
488    // b <- a + b
489    fn vec_znx_add_inplace<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T) {
490        unsafe {
491            vec_znx::vec_znx_add(
492                self.0,
493                b.as_mut_ptr(),
494                b.cols() as u64,
495                b.n() as u64,
496                a.as_ptr(),
497                a.cols() as u64,
498                a.n() as u64,
499                b.as_ptr(),
500                b.cols() as u64,
501                b.n() as u64,
502            )
503        }
504    }
505
506    // c <- a + b
507    fn vec_znx_sub<T: VecZnxApi + Infos>(&self, c: &mut T, a: &T, b: &T) {
508        unsafe {
509            vec_znx::vec_znx_sub(
510                self.0,
511                c.as_mut_ptr(),
512                c.cols() as u64,
513                c.n() as u64,
514                a.as_ptr(),
515                a.cols() as u64,
516                a.n() as u64,
517                b.as_ptr(),
518                b.cols() as u64,
519                b.n() as u64,
520            )
521        }
522    }
523
524    // b <- a + b
525    fn vec_znx_sub_inplace<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T) {
526        unsafe {
527            vec_znx::vec_znx_sub(
528                self.0,
529                b.as_mut_ptr(),
530                b.cols() as u64,
531                b.n() as u64,
532                a.as_ptr(),
533                a.cols() as u64,
534                a.n() as u64,
535                b.as_ptr(),
536                b.cols() as u64,
537                b.n() as u64,
538            )
539        }
540    }
541
542    fn vec_znx_negate<T: VecZnxApi + Infos>(&self, b: &mut T, a: &T) {
543        unsafe {
544            vec_znx::vec_znx_negate(
545                self.0,
546                b.as_mut_ptr(),
547                b.cols() as u64,
548                b.n() as u64,
549                a.as_ptr(),
550                a.cols() as u64,
551                a.n() as u64,
552            )
553        }
554    }
555
556    fn vec_znx_negate_inplace<T: VecZnxApi + Infos>(&self, a: &mut T) {
557        unsafe {
558            vec_znx::vec_znx_negate(
559                self.0,
560                a.as_mut_ptr(),
561                a.cols() as u64,
562                a.n() as u64,
563                a.as_ptr(),
564                a.cols() as u64,
565                a.n() as u64,
566            )
567        }
568    }
569
570    fn vec_znx_rotate<T: VecZnxApi + Infos>(&self, k: i64, a: &mut T, b: &T) {
571        unsafe {
572            vec_znx::vec_znx_rotate(
573                self.0,
574                k,
575                a.as_mut_ptr(),
576                a.cols() as u64,
577                a.n() as u64,
578                b.as_ptr(),
579                b.cols() as u64,
580                b.n() as u64,
581            )
582        }
583    }
584
585    fn vec_znx_rotate_inplace<T: VecZnxApi + Infos>(&self, k: i64, a: &mut T) {
586        unsafe {
587            vec_znx::vec_znx_rotate(
588                self.0,
589                k,
590                a.as_mut_ptr(),
591                a.cols() as u64,
592                a.n() as u64,
593                a.as_ptr(),
594                a.cols() as u64,
595                a.n() as u64,
596            )
597        }
598    }
599
600    /// Maps X^i to X^{ik} mod X^{n}+1. The mapping is applied independently on each limbs.
601    ///
602    /// # Arguments
603    ///
604    /// * `a`: input.
605    /// * `b`: output.
606    /// * `k`: the power to which to map each coefficients.
607    /// * `limbs_a`: the number of limbs_a on which to apply the mapping.
608    ///
609    /// # Panics
610    ///
611    /// The method will panic if the argument `limbs_a` is greater than `a.limbs()`.
612    ///
613    /// # Example
614    /// ```
615    /// use base2k::{Module, FFT64, VecZnx, Encoding, Infos, VecZnxApi, VecZnxOps};
616    /// use itertools::izip;
617    ///
618    /// let n: usize = 8; // polynomial degree
619    /// let module = Module::new::<FFT64>(n);
620    /// let mut a: VecZnx = module.new_vec_znx(2);
621    /// let mut b: VecZnx = module.new_vec_znx(2);
622    /// let mut c: VecZnx = module.new_vec_znx(2);
623    ///
624    /// (0..a.limbs()).for_each(|i|{
625    ///     a.at_mut(i).iter_mut().enumerate().for_each(|(i, x)|{
626    ///         *x = i as i64
627    ///     })
628    /// });
629    ///
630    /// module.vec_znx_automorphism(-1, &mut b, &a, 1); // X^i -> X^(-i)
631    /// let limb = c.at_mut(0);
632    /// (1..limb.len()).for_each(|i|{
633    ///     limb[n-i] = -(i as i64)
634    /// });
635    /// izip!(b.data.iter(), c.data.iter()).for_each(|(a, b)| assert_eq!(a, b, "{} != {}", a, b));
636    /// ```
637    fn vec_znx_automorphism<T: VecZnxApi + Infos>(&self, k: i64, b: &mut T, a: &T, limbs_a: usize) {
638        assert_eq!(a.n(), self.n());
639        assert_eq!(b.n(), self.n());
640        assert!(a.cols() >= limbs_a);
641        unsafe {
642            vec_znx::vec_znx_automorphism(
643                self.0,
644                k,
645                b.as_mut_ptr(),
646                b.cols() as u64,
647                b.n() as u64,
648                a.as_ptr(),
649                limbs_a as u64,
650                a.n() as u64,
651            );
652        }
653    }
654
655    /// Maps X^i to X^{ik} mod X^{n}+1. The mapping is applied independently on each limbs.
656    ///
657    /// # Arguments
658    ///
659    /// * `a`: input and output.
660    /// * `k`: the power to which to map each coefficients.
661    /// * `limbs_a`: the number of limbs on which to apply the mapping.
662    ///
663    /// # Panics
664    ///
665    /// The method will panic if the argument `limbs` is greater than `self.limbs()`.
666    ///
667    /// # Example
668    /// ```
669    /// use base2k::{Module, FFT64, VecZnx, Encoding, Infos, VecZnxApi, VecZnxOps};
670    /// use itertools::izip;
671    ///
672    /// let n: usize = 8; // polynomial degree
673    /// let module = Module::new::<FFT64>(n);
674    /// let mut a: VecZnx = VecZnx::new(n, 2);
675    /// let mut b: VecZnx = VecZnx::new(n, 2);
676    ///
677    /// (0..a.limbs()).for_each(|i|{
678    ///     a.at_mut(i).iter_mut().enumerate().for_each(|(i, x)|{
679    ///         *x = i as i64
680    ///     })
681    /// });
682    ///
683    /// module.vec_znx_automorphism_inplace(-1, &mut a, 1); // X^i -> X^(-i)
684    /// let limb = b.at_mut(0);
685    /// (1..limb.len()).for_each(|i|{
686    ///     limb[n-i] = -(i as i64)
687    /// });
688    /// izip!(a.data.iter(), b.data.iter()).for_each(|(a, b)| assert_eq!(a, b, "{} != {}", a, b));
689    /// ```
690    fn vec_znx_automorphism_inplace<T: VecZnxApi + Infos>(
691        &self,
692        k: i64,
693        a: &mut T,
694        limbs_a: usize,
695    ) {
696        assert_eq!(a.n(), self.n());
697        assert!(a.cols() >= limbs_a);
698        unsafe {
699            vec_znx::vec_znx_automorphism(
700                self.0,
701                k,
702                a.as_mut_ptr(),
703                a.cols() as u64,
704                a.n() as u64,
705                a.as_ptr(),
706                limbs_a as u64,
707                a.n() as u64,
708            );
709        }
710    }
711
712    fn vec_znx_split<T: VecZnxApi + Infos>(&self, b: &mut Vec<T>, a: &T, buf: &mut T) {
713        let (n_in, n_out) = (a.n(), b[0].n());
714
715        assert!(
716            n_out < n_in,
717            "invalid a: output ring degree should be smaller"
718        );
719        b[1..].iter().for_each(|bi| {
720            assert_eq!(
721                bi.n(),
722                n_out,
723                "invalid input a: all VecZnx must have the same degree"
724            )
725        });
726
727        b.iter_mut().enumerate().for_each(|(i, bi)| {
728            if i == 0 {
729                switch_degree(bi, a);
730                self.vec_znx_rotate(-1, buf, a);
731            } else {
732                switch_degree(bi, buf);
733                self.vec_znx_rotate_inplace(-1, buf);
734            }
735        })
736    }
737
738    fn vec_znx_merge<T: VecZnxApi + Infos>(&self, b: &mut T, a: &Vec<T>) {
739        let (n_in, n_out) = (b.n(), a[0].n());
740
741        assert!(
742            n_out < n_in,
743            "invalid a: output ring degree should be smaller"
744        );
745        a[1..].iter().for_each(|ai| {
746            assert_eq!(
747                ai.n(),
748                n_out,
749                "invalid input a: all VecZnx must have the same degree"
750            )
751        });
752
753        a.iter().enumerate().for_each(|(_, ai)| {
754            switch_degree(b, ai);
755            self.vec_znx_rotate_inplace(-1, b);
756        });
757
758        self.vec_znx_rotate_inplace(a.len() as i64, b);
759    }
760}