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