arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/blind_rotation.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/blind_rotation.rsRaw
  1use poulpy_core::{
  2    GLWECopy, GLWERotate, ScratchTakeCore,
  3    layouts::{GGSW, GGSWInfos, GGSWToMut, GGSWToRef, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, LWEInfos},
  4};
  5use poulpy_hal::{
  6    api::{VecZnxAddScalarInplace, VecZnxNormalizeInplace},
  7    layouts::{Backend, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxZero},
  8};
  9
 10use crate::tfhe::bdd_arithmetic::{Cmux, GetGGSWBit, UnsignedInteger};
 11
 12impl<T: UnsignedInteger, BE: Backend> GGSWBlindRotation<T, BE> for Module<BE>
 13where
 14    Self: GLWEBlindRotation<T, BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
 15    Scratch<BE>: ScratchTakeCore<BE>,
 16{
 17}
 18
 19pub trait GGSWBlindRotation<T: UnsignedInteger, BE: Backend>
 20where
 21    Self: GLWEBlindRotation<T, BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
 22    Scratch<BE>: ScratchTakeCore<BE>,
 23{
 24    fn ggsw_to_ggsw_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
 25    where
 26        R: GLWEInfos,
 27        K: GGSWInfos,
 28    {
 29        self.glwe_to_glwe_blind_rotation_tmp_bytes(res_infos, k_infos)
 30    }
 31
 32    #[allow(clippy::too_many_arguments)]
 33    /// res <- a * X^{((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
 34    fn ggsw_to_ggsw_blind_rotation<R, A, K>(
 35        &self,
 36        res: &mut R,
 37        a: &A,
 38        k: &K,
 39        sign: bool,
 40        bit_rsh: usize,
 41        bit_mask: usize,
 42        bit_lsh: usize,
 43        scratch: &mut Scratch<BE>,
 44    ) where
 45        R: GGSWToMut,
 46        A: GGSWToRef,
 47        K: GetGGSWBit<T, BE>,
 48        Scratch<BE>: ScratchTakeCore<BE>,
 49    {
 50        let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
 51        let a: &GGSW<&[u8]> = &a.to_ref();
 52
 53        assert!(res.dnum() <= a.dnum());
 54        assert_eq!(res.dsize(), a.dsize());
 55
 56        for col in 0..(res.rank() + 1).into() {
 57            for row in 0..res.dnum().into() {
 58                self.glwe_to_glwe_blind_rotation(
 59                    &mut res.at_mut(row, col),
 60                    &a.at(row, col),
 61                    k,
 62                    sign,
 63                    bit_rsh,
 64                    bit_mask,
 65                    bit_lsh,
 66                    scratch,
 67                );
 68            }
 69        }
 70    }
 71
 72    fn scalar_to_ggsw_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
 73    where
 74        R: GLWEInfos,
 75        K: GGSWInfos,
 76    {
 77        self.glwe_to_glwe_blind_rotation_tmp_bytes(res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
 78    }
 79
 80    #[allow(clippy::too_many_arguments)]
 81    fn scalar_to_ggsw_blind_rotation<R, S, K>(
 82        &self,
 83        res: &mut R,
 84        test_vector: &S,
 85        k: &K,
 86        sign: bool,
 87        bit_rsh: usize,
 88        bit_mask: usize,
 89        bit_lsh: usize,
 90        scratch: &mut Scratch<BE>,
 91    ) where
 92        R: GGSWToMut,
 93        S: ScalarZnxToRef,
 94        K: GetGGSWBit<T, BE>,
 95        Scratch<BE>: ScratchTakeCore<BE>,
 96    {
 97        let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
 98        let test_vector: &ScalarZnx<&[u8]> = &test_vector.to_ref();
 99
100        let base2k: usize = res.base2k().into();
101        let dsize: usize = res.dsize().into();
102
103        let (mut tmp_glwe, scratch_1) = scratch.take_glwe(res);
104
105        for col in 0..(res.rank() + 1).into() {
106            for row in 0..res.dnum().into() {
107                tmp_glwe.data_mut().zero();
108                self.vec_znx_add_scalar_inplace(
109                    tmp_glwe.data_mut(),
110                    col,
111                    (dsize - 1) + row * dsize,
112                    test_vector,
113                    0,
114                );
115                self.vec_znx_normalize_inplace(base2k, tmp_glwe.data_mut(), col, scratch_1);
116
117                self.glwe_to_glwe_blind_rotation(
118                    &mut res.at_mut(row, col),
119                    &tmp_glwe,
120                    k,
121                    sign,
122                    bit_rsh,
123                    bit_mask,
124                    bit_lsh,
125                    scratch_1,
126                );
127            }
128        }
129    }
130}
131
132impl<T: UnsignedInteger, BE: Backend> GLWEBlindRotation<T, BE> for Module<BE>
133where
134    Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
135    Scratch<BE>: ScratchTakeCore<BE>,
136{
137}
138
139pub trait GLWEBlindRotation<T: UnsignedInteger, BE: Backend>
140where
141    Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
142    Scratch<BE>: ScratchTakeCore<BE>,
143{
144    fn glwe_to_glwe_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
145    where
146        R: GLWEInfos,
147        K: GGSWInfos,
148    {
149        self.cmux_tmp_bytes(res_infos, res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
150    }
151
152    #[allow(clippy::too_many_arguments)]
153    /// res <- a * X^{sign * ((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
154    fn glwe_to_glwe_blind_rotation<R, A, K>(
155        &self,
156        res: &mut R,
157        a: &A,
158        k: &K,
159        sign: bool,
160        bit_rsh: usize,
161        bit_mask: usize,
162        bit_lsh: usize,
163        scratch: &mut Scratch<BE>,
164    ) where
165        R: GLWEToMut,
166        A: GLWEToRef,
167        K: GetGGSWBit<T, BE>,
168        Scratch<BE>: ScratchTakeCore<BE>,
169    {
170        assert!(bit_rsh + bit_mask <= T::WORD_SIZE);
171
172        let mut res: GLWE<&mut [u8]> = res.to_mut();
173        let a: &GLWE<&[u8]> = &a.to_ref();
174
175        let (mut tmp_res, scratch_1) = scratch.take_glwe(&res);
176
177        // a <- a ; b <- a * X^{-2^{i + bit_lsh}}
178        match sign {
179            true => self.glwe_rotate(1 << bit_lsh, &mut res, a),
180            false => self.glwe_rotate(-1 << bit_lsh, &mut res, a),
181        }
182
183        // b <- (b - a) * GGSW(b[i]) + a
184        self.cmux_inplace(&mut res, a, &k.get_bit(bit_rsh), scratch_1);
185
186        // a_is_res = true  => (a, b) = (&mut res, &mut tmp_res)
187        // a_is_res = false => (a, b) = (&mut tmp_res, &mut res)
188        let mut a_is_res: bool = true;
189
190        for i in 1..bit_mask {
191            let (a, b) = if a_is_res {
192                (&mut res, &mut tmp_res)
193            } else {
194                (&mut tmp_res, &mut res)
195            };
196
197            // a <- a ; b <- a * X^{-2^{i + bit_lsh}}
198            match sign {
199                true => self.glwe_rotate(1 << (i + bit_lsh), b, a),
200                false => self.glwe_rotate(-1 << (i + bit_lsh), b, a),
201            }
202
203            // b <- (b - a) * GGSW(b[i]) + a
204            self.cmux_inplace(b, a, &k.get_bit(i + bit_rsh), scratch_1);
205
206            // ping-pong roles for next iter
207            a_is_res = !a_is_res;
208        }
209
210        // Ensure the final value ends up in `res`
211        if !a_is_res {
212            self.glwe_copy(&mut res, &tmp_res);
213        }
214    }
215}