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 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.cols() << 3.
54 ///
55 /// # Panics
56 ///
57 /// The method will panic if carry.len() < self.n() * self.cols() << 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, cols: usize, n: usize);
71}
72
73pub fn bytes_of_vec_znx(n: usize, cols: usize) -> usize {
74 n * cols * 8
75}
76
77pub struct VecZnxBorrow {
78 pub n: usize,
79 pub cols: 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, cols: usize, bytes: &mut [u8]) -> Self::Owned {
93 let size = Self::bytes_of(n, cols);
94 assert!(
95 bytes.len() >= size,
96 "invalid buffer: buf.len()={} < self.buffer_size(n={}, cols={})={}",
97 bytes.len(),
98 n,
99 cols,
100 size
101 );
102 VecZnxBorrow {
103 n: n,
104 cols: cols,
105 data: cast_mut(&mut bytes[..size]).as_mut_ptr(),
106 }
107 }
108
109 fn bytes_of(n: usize, cols: usize) -> usize {
110 bytes_of_vec_znx(n, cols)
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.cols) }
123 }
124
125 fn raw_mut(&mut self) -> &mut [i64] {
126 unsafe { std::slice::from_raw_parts_mut(self.data, self.n * self.cols) }
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.cols) 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, cols: usize, n: usize) {
169 (0..cols).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, cols: usize, buf: &mut [u8]) -> Self::Owned {
183 let size = Self::bytes_of(n, cols);
184 assert!(
185 buf.len() >= size,
186 "invalid buffer: buf.len()={} < self.buffer_size(n={}, cols={})={}",
187 buf.len(),
188 n,
189 cols,
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, cols: usize) -> usize {
200 bytes_of_vec_znx(n, cols)
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, cols: usize, n: usize) {
257 (0..cols).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 col is equally spaced by n.
269 pub data: Vec<i64>,
270}
271
272impl VecZnx {
273 /// Allocates a new [VecZnx] composed of #cols polynomials of Z\[X\].
274 pub fn new(n: usize, cols: usize) -> Self {
275 Self {
276 n: n,
277 data: alloc_aligned::<i64>(n * cols, 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 cols = min(a.cols(), b.cols());
325
326 (0..cols).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 cols: usize = a.cols();
373 let cols_steps: usize = k / log_base2k;
374
375 a.raw_mut().rotate_right(n * cols_steps);
376 unsafe {
377 znx::znx_zero_i64_ref((n * cols_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 (cols_steps..cols).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 /// * `cols`: the number of cols.
408 fn new_vec_znx(&self, cols: 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, cols: 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_cols: 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_cols: 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, cols: usize) -> VecZnx {
463 VecZnx::new(self.n(), cols)
464 }
465
466 fn bytes_of_vec_znx(&self, cols: usize) -> usize {
467 self.n() * cols * 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 cols.
601 ///
602 /// # Arguments
603 ///
604 /// * `a`: input.
605 /// * `b`: output.
606 /// * `k`: the power to which to map each coefficients.
607 /// * `a_cols`: the number of a_cols on which to apply the mapping.
608 ///
609 /// # Panics
610 ///
611 /// The method will panic if the argument `a` is greater than `a.cols()`.
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.cols()).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 col = c.at_mut(0);
632 /// (1..col.len()).for_each(|i|{
633 /// col[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, a_cols: usize) {
638 assert_eq!(a.n(), self.n());
639 assert_eq!(b.n(), self.n());
640 assert!(a.cols() >= a_cols);
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 a_cols 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 cols.
656 ///
657 /// # Arguments
658 ///
659 /// * `a`: input and output.
660 /// * `k`: the power to which to map each coefficients.
661 /// * `a_cols`: the number of cols on which to apply the mapping.
662 ///
663 /// # Panics
664 ///
665 /// The method will panic if the argument `cols` is greater than `self.cols()`.
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.cols()).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 col = b.at_mut(0);
685 /// (1..col.len()).for_each(|i|{
686 /// col[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>(&self, k: i64, a: &mut T, a_cols: usize) {
691 assert_eq!(a.n(), self.n());
692 assert!(a.cols() >= a_cols);
693 unsafe {
694 vec_znx::vec_znx_automorphism(
695 self.0,
696 k,
697 a.as_mut_ptr(),
698 a.cols() as u64,
699 a.n() as u64,
700 a.as_ptr(),
701 a_cols as u64,
702 a.n() as u64,
703 );
704 }
705 }
706
707 fn vec_znx_split<T: VecZnxApi + Infos>(&self, b: &mut Vec<T>, a: &T, buf: &mut T) {
708 let (n_in, n_out) = (a.n(), b[0].n());
709
710 assert!(
711 n_out < n_in,
712 "invalid a: output ring degree should be smaller"
713 );
714 b[1..].iter().for_each(|bi| {
715 assert_eq!(
716 bi.n(),
717 n_out,
718 "invalid input a: all VecZnx must have the same degree"
719 )
720 });
721
722 b.iter_mut().enumerate().for_each(|(i, bi)| {
723 if i == 0 {
724 switch_degree(bi, a);
725 self.vec_znx_rotate(-1, buf, a);
726 } else {
727 switch_degree(bi, buf);
728 self.vec_znx_rotate_inplace(-1, buf);
729 }
730 })
731 }
732
733 fn vec_znx_merge<T: VecZnxApi + Infos>(&self, b: &mut T, a: &Vec<T>) {
734 let (n_in, n_out) = (b.n(), a[0].n());
735
736 assert!(
737 n_out < n_in,
738 "invalid a: output ring degree should be smaller"
739 );
740 a[1..].iter().for_each(|ai| {
741 assert_eq!(
742 ai.n(),
743 n_out,
744 "invalid input a: all VecZnx must have the same degree"
745 )
746 });
747
748 a.iter().enumerate().for_each(|(_, ai)| {
749 switch_degree(b, ai);
750 self.vec_znx_rotate_inplace(-1, b);
751 });
752
753 self.vec_znx_rotate_inplace(a.len() as i64, b);
754 }
755}