arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/blind_rotation/lut.rs
core/src/blind_rotation/lut.rsRaw
  1use backend::{FFT64, Module, ScalarZnx, VecZnx, VecZnxAlloc, VecZnxOps, ZnxInfos, ZnxView, 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        let size: usize = k.div_ceil(basek);
 12        let mut data: Vec<VecZnx<Vec<u8>>> = Vec::with_capacity(extension_factor);
 13        (0..extension_factor).for_each(|_| {
 14            data.push(module.new_vec_znx(1, size));
 15        });
 16        Self { data, basek, k }
 17    }
 18
 19    pub fn extension_factor(&self) -> usize {
 20        self.data.len()
 21    }
 22
 23    pub fn domain_size(&self) -> usize {
 24        self.data.len() * self.data[0].n()
 25    }
 26
 27    pub fn set(&mut self, module: &Module<FFT64>, f: fn(i64) -> i64, message_modulus: usize) {
 28        let basek: usize = self.basek;
 29
 30        // Get the number minimum limb to store the message modulus
 31        let limbs: usize = message_modulus.div_ceil(1 << basek);
 32
 33        // Scaling factor
 34        let scale: i64 = (1 << (basek * limbs - 1)).div_round(message_modulus) as i64;
 35
 36        // Updates function
 37        let f_scaled = |x: i64| (f(x) % message_modulus as i64) * scale;
 38
 39        // If LUT size > module.n()
 40        let domain_size: usize = self.domain_size();
 41
 42        let size: usize = self.k.div_ceil(self.basek);
 43
 44        // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
 45        let mut lut_full: VecZnx<Vec<u8>> = VecZnx::new::<i64>(domain_size, 1, size);
 46        {
 47            let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1);
 48
 49            let start: usize = 0;
 50            let end: usize = (domain_size).div_round(message_modulus);
 51
 52            let y: i64 = f_scaled(0);
 53            (start..end).for_each(|i| {
 54                lut_at[i] = y;
 55            });
 56
 57            (1..message_modulus).for_each(|x| {
 58                let start: usize = (x * domain_size).div_round(message_modulus);
 59                let end: usize = ((x + 1) * domain_size).div_round(message_modulus);
 60                let y: i64 = f_scaled(x as i64);
 61                (start..end).for_each(|i| {
 62                    lut_at[i] = y;
 63                })
 64            });
 65        }
 66
 67        // Rotates half the step to the left
 68        let half_step: usize = domain_size.div_round(message_modulus << 1);
 69
 70        lut_full.rotate(-(half_step as i64));
 71
 72        let mut tmp_bytes: Vec<u8> = alloc_aligned(lut_full.n() * size_of::<i64>());
 73        lut_full.normalize(self.basek, 0, &mut tmp_bytes);
 74
 75        if self.extension_factor() > 1 {
 76            (0..self.extension_factor()).for_each(|i| {
 77                module.switch_degree(&mut self.data[i], 0, &lut_full, 0);
 78                if i < self.extension_factor() {
 79                    lut_full.rotate(-1);
 80                }
 81            });
 82        } else {
 83            module.vec_znx_copy(&mut self.data[0], 0, &lut_full, 0);
 84        }
 85    }
 86
 87    pub fn set_raw<D>(&mut self, module: &Module<FFT64>, lut: &ScalarZnx<D>)
 88    where
 89        D: AsRef<[u8]>,
 90    {
 91        let domain_size: usize = self.domain_size();
 92
 93        let size: usize = self.k.div_ceil(self.basek);
 94
 95        let mut lut_full: VecZnx<Vec<u8>> = VecZnx::new::<i64>(domain_size, 1, size);
 96
 97        lut_full.at_mut(0, 0).copy_from_slice(lut.raw());
 98
 99        if self.extension_factor() > 1 {
100            (0..self.extension_factor()).for_each(|i| {
101                module.switch_degree(&mut self.data[i], 0, &lut_full, 0);
102                if i < self.extension_factor() {
103                    lut_full.rotate(-1);
104                }
105            });
106        } else {
107            module.vec_znx_copy(&mut self.data[0], 0, &lut_full, 0);
108        }
109    }
110
111    #[allow(dead_code)]
112    pub(crate) fn rotate(&mut self, k: i64) {
113        let extension_factor: usize = self.extension_factor();
114        let two_n: usize = 2 * self.data[0].n();
115        let two_n_ext: usize = two_n * extension_factor;
116
117        let k_pos: usize = ((k + two_n_ext as i64) % two_n_ext as i64) as usize;
118
119        let k_hi: usize = k_pos / extension_factor;
120        let k_lo: usize = k_pos % extension_factor;
121
122        (0..extension_factor - k_lo).for_each(|i| {
123            self.data[i].rotate(k_hi as i64);
124        });
125
126        (extension_factor - k_lo..extension_factor).for_each(|i| {
127            self.data[i].rotate(k_hi as i64 + 1);
128        });
129
130        self.data.rotate_right(k_lo as usize);
131    }
132}
133
134pub(crate) trait DivRound {
135    fn div_round(self, rhs: Self) -> Self;
136}
137
138impl DivRound for usize {
139    #[inline]
140    fn div_round(self, rhs: Self) -> Self {
141        (self + rhs / 2) / rhs
142    }
143}