arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/blind_rotation/lut.rs
core/src/blind_rotation/lut.rsRaw
  1use backend::hal::{
  2    api::{
  3        ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxCopy, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotateInplace,
  4        VecZnxSwithcDegree, ZnxInfos, ZnxViewMut,
  5    },
  6    layouts::{Backend, Module, ScratchOwned, VecZnx},
  7    oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl},
  8};
  9
 10pub struct LookUpTable {
 11    pub(crate) data: Vec<VecZnx<Vec<u8>>>,
 12    pub(crate) basek: usize,
 13    pub(crate) k: usize,
 14}
 15
 16impl LookUpTable {
 17    pub fn alloc(n: usize, basek: usize, k: usize, extension_factor: usize) -> Self {
 18        #[cfg(debug_assertions)]
 19        {
 20            assert!(
 21                extension_factor & (extension_factor - 1) == 0,
 22                "extension_factor must be a power of two but is: {}",
 23                extension_factor
 24            );
 25        }
 26        let size: usize = k.div_ceil(basek);
 27        let mut data: Vec<VecZnx<Vec<u8>>> = Vec::with_capacity(extension_factor);
 28        (0..extension_factor).for_each(|_| {
 29            data.push(VecZnx::alloc(n, 1, size));
 30        });
 31        Self { data, basek, k }
 32    }
 33
 34    pub fn log_extension_factor(&self) -> usize {
 35        (usize::BITS - (self.extension_factor() - 1).leading_zeros()) as _
 36    }
 37
 38    pub fn extension_factor(&self) -> usize {
 39        self.data.len()
 40    }
 41
 42    pub fn domain_size(&self) -> usize {
 43        self.data.len() * self.data[0].n()
 44    }
 45
 46    pub fn set<B: Backend>(&mut self, module: &Module<B>, f: &Vec<i64>, k: usize)
 47    where
 48        Module<B>: VecZnxRotateInplace + VecZnxNormalizeInplace<B> + VecZnxNormalizeTmpBytes + VecZnxSwithcDegree + VecZnxCopy,
 49        B: ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
 50    {
 51        assert!(f.len() <= module.n());
 52
 53        let basek: usize = self.basek;
 54
 55        // Get the number minimum limb to store the message modulus
 56        let limbs: usize = k.div_ceil(1 << basek);
 57
 58        #[cfg(debug_assertions)]
 59        {
 60            assert!(limbs <= self.data[0].size());
 61        }
 62
 63        // Scaling factor
 64        let scale: i64 = 1 << (k % basek) as i64;
 65
 66        // #elements in lookup table
 67        let f_len: usize = f.len();
 68
 69        // If LUT size > TakeScalarZnx
 70        let domain_size: usize = self.domain_size();
 71
 72        let size: usize = self.k.div_ceil(self.basek);
 73
 74        // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
 75        let mut lut_full: VecZnx<Vec<u8>> = VecZnx::alloc(domain_size, 1, size);
 76
 77        let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1);
 78
 79        f.iter().enumerate().for_each(|(i, fi)| {
 80            let start: usize = (i * domain_size).div_round(f_len);
 81            let end: usize = ((i + 1) * domain_size).div_round(f_len);
 82            lut_at[start..end].fill(fi * scale);
 83        });
 84
 85        // Rotates half the step to the left
 86        let half_step: usize = domain_size.div_round(f_len << 1);
 87
 88        module.vec_znx_rotate_inplace(-(half_step as i64), &mut lut_full, 0);
 89
 90        let n_large: usize = lut_full.n();
 91
 92        module.vec_znx_normalize_inplace(
 93            self.basek,
 94            &mut lut_full,
 95            0,
 96            ScratchOwned::alloc(module.vec_znx_normalize_tmp_bytes(n_large)).borrow(),
 97        );
 98
 99        if self.extension_factor() > 1 {
100            (0..self.extension_factor()).for_each(|i| {
101                module.vec_znx_switch_degree(&mut self.data[i], 0, &lut_full, 0);
102                if i < self.extension_factor() {
103                    module.vec_znx_rotate_inplace(-1, &mut lut_full, 0);
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<B: Backend>(&mut self, module: &Module<B>, k: i64)
113    where
114        Module<B>: VecZnxRotateInplace,
115    {
116        let extension_factor: usize = self.extension_factor();
117        let two_n: usize = 2 * self.data[0].n();
118        let two_n_ext: usize = two_n * extension_factor;
119
120        let k_pos: usize = ((k + two_n_ext as i64) % two_n_ext as i64) as usize;
121
122        let k_hi: usize = k_pos / extension_factor;
123        let k_lo: usize = k_pos % extension_factor;
124
125        (0..extension_factor - k_lo).for_each(|i| {
126            module.vec_znx_rotate_inplace(k_hi as i64, &mut self.data[i], 0);
127        });
128
129        (extension_factor - k_lo..extension_factor).for_each(|i| {
130            module.vec_znx_rotate_inplace(k_hi as i64 + 1, &mut self.data[i], 0);
131        });
132
133        self.data.rotate_right(k_lo as usize);
134    }
135}
136
137pub(crate) trait DivRound {
138    fn div_round(self, rhs: Self) -> Self;
139}
140
141impl DivRound for usize {
142    #[inline]
143    fn div_round(self, rhs: Self) -> Self {
144        (self + rhs / 2) / rhs
145    }
146}