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