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