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