arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/bin_fhe/bdd_arithmetic/blind_retrieval.rs
poulpy-schemes/src/bin_fhe/bdd_arithmetic/blind_retrieval.rsRaw
  1use itertools::Itertools;
  2use poulpy_core::{
  3    GLWECopy, ScratchTakeCore,
  4    layouts::{GGSWInfos, GGSWPrepared, GLWE, GLWEInfos, GLWEToMut, GLWEToRef},
  5};
  6use poulpy_hal::layouts::{Backend, Module, Scratch};
  7
  8use crate::bin_fhe::bdd_arithmetic::{Cmux, Cswap, GetGGSWBit};
  9
 10pub struct GLWEBlindRetriever {
 11    accumulators: Vec<Accumulator>,
 12    counter: usize,
 13}
 14
 15impl GLWEBlindRetriever {
 16    pub fn alloc<A>(infos: &A, size: usize) -> Self
 17    where
 18        A: GLWEInfos,
 19    {
 20        let bit_size: usize = (u32::BITS - (size as u32 - 1).leading_zeros()) as usize;
 21        Self {
 22            accumulators: (0..bit_size).map(|_| Accumulator::alloc(infos)).collect_vec(),
 23            counter: 0,
 24        }
 25    }
 26
 27    pub fn retrieve_tmp_bytes<M, R, S, BE: Backend>(module: &M, res: &R, selector: &S) -> usize
 28    where
 29        M: Cmux<BE>,
 30        R: GLWEInfos,
 31        S: GGSWInfos,
 32    {
 33        module.cmux_tmp_bytes(res, res, selector)
 34    }
 35
 36    pub fn retrieve<M, R, A, S, BE: Backend>(
 37        &mut self,
 38        module: &M,
 39        res: &mut R,
 40        data: &[A],
 41        selector: &S,
 42        offset: usize,
 43        scratch: &mut Scratch<BE>,
 44    ) where
 45        M: GLWECopy + Cmux<BE>,
 46        R: GLWEToMut,
 47        A: GLWEToRef,
 48        S: GetGGSWBit<BE>,
 49        Scratch<BE>: ScratchTakeCore<BE>,
 50    {
 51        self.reset();
 52        for ct in data {
 53            self.add(module, ct, selector, offset, scratch);
 54        }
 55        self.flush(module, res, selector, offset, scratch);
 56    }
 57
 58    pub fn add<A, S, M, BE: Backend>(&mut self, module: &M, a: &A, selector: &S, offset: usize, scratch: &mut Scratch<BE>)
 59    where
 60        A: GLWEToRef,
 61        S: GetGGSWBit<BE>,
 62        M: GLWECopy + Cmux<BE>,
 63        Scratch<BE>: ScratchTakeCore<BE>,
 64    {
 65        assert!(
 66            (self.counter as u32) < 1 << self.accumulators.len(),
 67            "Accumulating limit of {} reached",
 68            1 << self.accumulators.len()
 69        );
 70
 71        add_core(module, a, &mut self.accumulators, 0, selector, offset, scratch);
 72        self.counter += 1;
 73    }
 74
 75    pub fn flush<R, M, S, BE: Backend>(&mut self, module: &M, res: &mut R, selector: &S, offset: usize, scratch: &mut Scratch<BE>)
 76    where
 77        R: GLWEToMut,
 78        S: GetGGSWBit<BE>,
 79        M: GLWECopy + Cmux<BE>,
 80        Scratch<BE>: ScratchTakeCore<BE>,
 81    {
 82        for i in 0..self.accumulators.len() - 1 {
 83            let (acc_prev, acc_next) = self.accumulators.split_at_mut(i + 1);
 84            if acc_prev[i].num != 0 {
 85                add_core(module, &acc_prev[i].data, acc_next, i + 1, selector, offset, scratch);
 86                acc_prev[0].num = 0
 87            }
 88        }
 89        module.glwe_copy(res, &self.accumulators.last().unwrap().data);
 90        self.reset()
 91    }
 92
 93    fn reset(&mut self) {
 94        for acc in self.accumulators.iter_mut() {
 95            acc.num = 0;
 96        }
 97        self.counter = 0;
 98    }
 99}
100
101struct Accumulator {
102    data: GLWE<Vec<u8>>,
103    num: usize, // Number of accumulated values
104}
105
106impl Accumulator {
107    pub fn alloc<A>(infos: &A) -> Self
108    where
109        A: GLWEInfos,
110    {
111        Self {
112            data: GLWE::alloc_from_infos(infos),
113            num: 0,
114        }
115    }
116}
117
118fn add_core<A, S, M, BE: Backend>(
119    module: &M,
120    a: &A,
121    accumulators: &mut [Accumulator],
122    i: usize,
123    selector: &S,
124    offset: usize,
125    scratch: &mut Scratch<BE>,
126) where
127    A: GLWEToRef,
128    S: GetGGSWBit<BE>,
129    M: GLWECopy + Cmux<BE>,
130    Scratch<BE>: ScratchTakeCore<BE>,
131{
132    // Isolate the first accumulator
133    let (acc_prev, acc_next) = accumulators.split_at_mut(1);
134
135    match acc_prev[0].num {
136        0 => {
137            module.glwe_copy(&mut acc_prev[0].data, a);
138            acc_prev[0].num = 1;
139        }
140        1 => {
141            module.cmux_inplace_neg(&mut acc_prev[0].data, a, &selector.get_bit(i + offset), scratch);
142
143            if !acc_next.is_empty() {
144                add_core(module, &acc_prev[0].data, acc_next, i + 1, selector, offset, scratch);
145            }
146
147            acc_prev[0].num = 0
148        }
149        _ => {
150            panic!("something went wrong")
151        }
152    }
153}
154
155impl<BE: Backend> GLWEBlindRetrieval<BE> for Module<BE> where Self: GLWECopy + Cmux<BE> + Cswap<BE> {}
156
157pub trait GLWEBlindRetrieval<BE: Backend>
158where
159    Self: GLWECopy + Cmux<BE> + Cswap<BE>,
160{
161    fn glwe_blind_retrieval_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
162    where
163        R: GLWEInfos,
164        K: GGSWInfos,
165    {
166        self.cswap_tmp_bytes(res_infos, res_infos, k_infos)
167    }
168
169    fn glwe_blind_retrieval_statefull<R, K>(
170        &self,
171        res: &mut Vec<R>,
172        bits: &K,
173        bit_rsh: usize,
174        bit_mask: usize,
175        scratch: &mut Scratch<BE>,
176    ) where
177        R: GLWEToMut + GLWEInfos,
178        K: GetGGSWBit<BE>,
179        Scratch<BE>: ScratchTakeCore<BE>,
180    {
181        for i in 0..bit_mask {
182            let t: usize = 1 << (bit_mask - i - 1);
183            let bit: &GGSWPrepared<&[u8], BE> = &bits.get_bit(bit_rsh + bit_mask - i - 1); // MSB -> LSB traversal
184            for j in 0..t {
185                if j + t < res.len() {
186                    let (lo, hi) = res.split_at_mut(j + t);
187                    self.cswap(&mut lo[j], &mut hi[0], bit, scratch);
188                }
189            }
190        }
191    }
192
193    fn glwe_blind_retrieval_statefull_rev<R, K>(
194        &self,
195        res: &mut Vec<R>,
196        bits: &K,
197        bit_rsh: usize,
198        bit_mask: usize,
199        scratch: &mut Scratch<BE>,
200    ) where
201        R: GLWEToMut + GLWEInfos,
202        K: GetGGSWBit<BE>,
203        Scratch<BE>: ScratchTakeCore<BE>,
204    {
205        for i in (0..bit_mask).rev() {
206            let t: usize = 1 << (bit_mask - i - 1);
207            let bit: &GGSWPrepared<&[u8], BE> = &bits.get_bit(bit_rsh + bit_mask - i - 1); // MSB -> LSB traversal
208            for j in 0..t {
209                if j < res.len() && j + t < res.len() {
210                    let (lo, hi) = res.split_at_mut(j + t);
211                    self.cswap(&mut lo[j], &mut hi[0], bit, scratch);
212                }
213            }
214        }
215    }
216}