arnaucube
arnaucube/poulpyPublic Self-hosted repository
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}