poulpy/core/src/blind_rotation/lut.rs
core/src/blind_rotation/lut.rsRaw
1use backend::{FFT64, Module, VecZnx, VecZnxAlloc, VecZnxOps, ZnxInfos, ZnxViewMut, alloc_aligned};
2
3pub struct LookUpTable {
4 pub(crate) data: Vec<VecZnx<Vec<u8>>>,
5 pub(crate) basek: usize,
6 pub(crate) k: usize,
7}
8
9impl LookUpTable {
10 pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, extension_factor: usize) -> Self {
11 #[cfg(debug_assertions)]
12 {
13 assert!(
14 extension_factor & (extension_factor - 1) == 0,
15 "extension_factor must be a power of two but is: {}",
16 extension_factor
17 );
18 }
19 let size: usize = k.div_ceil(basek);
20 let mut data: Vec<VecZnx<Vec<u8>>> = Vec::with_capacity(extension_factor);
21 (0..extension_factor).for_each(|_| {
22 data.push(module.new_vec_znx(1, size));
23 });
24 Self { data, basek, k }
25 }
26
27 pub fn extension_factor(&self) -> usize {
28 self.data.len()
29 }
30
31 pub fn domain_size(&self) -> usize {
32 self.data.len() * self.data[0].n()
33 }
34
35 pub fn set(&mut self, module: &Module<FFT64>, f: &Vec<i64>, k: usize) {
36 assert!(f.len() <= module.n());
37
38 let basek: usize = self.basek;
39
40 // Get the number minimum limb to store the message modulus
41 let limbs: usize = k.div_ceil(1 << basek);
42
43 #[cfg(debug_assertions)]
44 {
45 assert!(limbs <= self.data[0].size());
46 }
47
48 // Scaling factor
49 let scale: i64 = 1 << (k % basek) as i64;
50
51 // #elements in lookup table
52 let f_len: usize = f.len();
53
54 // If LUT size > module.n()
55 let domain_size: usize = self.domain_size();
56
57 let size: usize = self.k.div_ceil(self.basek);
58
59 // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
60 let mut lut_full: VecZnx<Vec<u8>> = VecZnx::new::<i64>(domain_size, 1, size);
61
62 let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1);
63
64 f.iter().enumerate().for_each(|(i, fi)| {
65 let start: usize = (i * domain_size).div_round(f_len);
66 let end: usize = ((i + 1) * domain_size).div_round(f_len);
67 lut_at[start..end].fill(fi * scale);
68 });
69
70 // Rotates half the step to the left
71 let half_step: usize = domain_size.div_round(f_len << 1);
72
73 lut_full.rotate(-(half_step as i64));
74
75 let mut tmp_bytes: Vec<u8> = alloc_aligned(lut_full.n() * size_of::<i64>());
76 lut_full.normalize(self.basek, 0, &mut tmp_bytes);
77
78 if self.extension_factor() > 1 {
79 (0..self.extension_factor()).for_each(|i| {
80 module.switch_degree(&mut self.data[i], 0, &lut_full, 0);
81 if i < self.extension_factor() {
82 lut_full.rotate(-1);
83 }
84 });
85 } else {
86 module.vec_znx_copy(&mut self.data[0], 0, &lut_full, 0);
87 }
88 }
89
90 #[allow(dead_code)]
91 pub(crate) fn rotate(&mut self, k: i64) {
92 let extension_factor: usize = self.extension_factor();
93 let two_n: usize = 2 * self.data[0].n();
94 let two_n_ext: usize = two_n * extension_factor;
95
96 let k_pos: usize = ((k + two_n_ext as i64) % two_n_ext as i64) as usize;
97
98 let k_hi: usize = k_pos / extension_factor;
99 let k_lo: usize = k_pos % extension_factor;
100
101 (0..extension_factor - k_lo).for_each(|i| {
102 self.data[i].rotate(k_hi as i64);
103 });
104
105 (extension_factor - k_lo..extension_factor).for_each(|i| {
106 self.data[i].rotate(k_hi as i64 + 1);
107 });
108
109 self.data.rotate_right(k_lo as usize);
110 }
111}
112
113pub(crate) trait DivRound {
114 fn div_round(self, rhs: Self) -> Self;
115}
116
117impl DivRound for usize {
118 #[inline]
119 fn div_round(self, rhs: Self) -> Self {
120 (self + rhs / 2) / rhs
121 }
122}