arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/encoding.rs
base2k/src/encoding.rsRaw
  1use crate::ffi::znx::znx_zero_i64_ref;
  2use crate::{Infos, VecZnx, VecZnxApi};
  3use itertools::izip;
  4use std::cmp::min;
  5
  6pub trait Encoding {
  7    /// encode a vector of i64 on the receiver.
  8    ///
  9    /// # Arguments
 10    ///
 11    /// * `log_base2k`: base two logarithm decomposition of the receiver.
 12    /// * `log_k`: base two logarithm of the scaling of the data.
 13    /// * `data`: data to encode on the receiver.
 14    /// * `log_max`: base two logarithm of the infinity norm of the input data.
 15    fn encode_vec_i64(&mut self, log_base2k: usize, log_k: usize, data: &[i64], log_max: usize);
 16
 17    /// decode a vector of i64 from the receiver.
 18    ///
 19    /// # Arguments
 20    ///
 21    /// * `log_base2k`: base two logarithm decomposition of the receiver.
 22    /// * `log_k`: base two logarithm of the scaling of the data.
 23    /// * `data`: data to decode from the receiver.
 24    fn decode_vec_i64(&self, log_base2k: usize, log_k: usize, data: &mut [i64]);
 25
 26    /// encodes a single i64 on the receiver at the given index.
 27    ///
 28    /// # Arguments
 29    ///
 30    /// * `log_base2k`: base two logarithm decomposition of the receiver.
 31    /// * `log_k`: base two logarithm of the scaling of the data.
 32    /// * `i`: index of the coefficient on which to encode the data.
 33    /// * `data`: data to encode on the receiver.
 34    /// * `log_max`: base two logarithm of the infinity norm of the input data.
 35    fn encode_coeff_i64(
 36        &mut self,
 37        log_base2k: usize,
 38        log_k: usize,
 39        i: usize,
 40        data: i64,
 41        log_max: usize,
 42    );
 43
 44    /// decode a single of i64 from the receiver at the given index.
 45    ///
 46    /// # Arguments
 47    ///
 48    /// * `log_base2k`: base two logarithm decomposition of the receiver.
 49    /// * `log_k`: base two logarithm of the scaling of the data.
 50    /// * `i`: index of the coefficient to decode.
 51    /// * `data`: data to decode from the receiver.
 52    fn decode_coeff_i64(&self, log_base2k: usize, log_k: usize, i: usize) -> i64;
 53}
 54
 55impl Encoding for VecZnx {
 56    fn encode_vec_i64(&mut self, log_base2k: usize, log_k: usize, data: &[i64], log_max: usize) {
 57        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
 58
 59        println!("limbs: {}", limbs);
 60
 61        assert!(limbs <= self.limbs(), "invalid argument log_k: (log_k + self.log_base2k - 1)/self.log_base2k={} > self.limbs()={}", limbs, self.limbs());
 62
 63        let size: usize = min(data.len(), self.n());
 64        let log_k_rem: usize = log_base2k - (log_k % log_base2k);
 65
 66        // If 2^{log_base2k} * 2^{k_rem} < 2^{63}-1, then we can simply copy
 67        // values on the last limb.
 68        // Else we decompose values base2k.
 69        if log_max + log_k_rem < 63 || log_k_rem == log_base2k {
 70            (0..self.limbs()).for_each(|i| unsafe {
 71                znx_zero_i64_ref(size as u64, self.at_mut(i).as_mut_ptr());
 72            });
 73            self.at_mut(limbs - 1)[..size].copy_from_slice(&data[..size]);
 74        } else {
 75            let mask: i64 = (1 << log_base2k) - 1;
 76            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
 77
 78            (0..steps).for_each(|i| unsafe {
 79                znx_zero_i64_ref(size as u64, self.at_mut(i).as_mut_ptr());
 80            });
 81
 82            (limbs - steps..limbs)
 83                .rev()
 84                .enumerate()
 85                .for_each(|(i, i_rev)| {
 86                    let shift: usize = i * log_base2k;
 87                    izip!(self.at_mut(i_rev)[..size].iter_mut(), data[..size].iter())
 88                        .for_each(|(y, x)| *y = (x >> shift) & mask);
 89                })
 90        }
 91
 92        // Case where self.prec % self.k != 0.
 93        if log_k_rem != log_base2k {
 94            let limbs = self.limbs();
 95            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
 96            (limbs - steps..limbs).rev().for_each(|i| {
 97                self.at_mut(i)[..size]
 98                    .iter_mut()
 99                    .for_each(|x| *x <<= log_k_rem);
100            })
101        }
102    }
103
104    fn decode_vec_i64(&self, log_base2k: usize, log_k: usize, data: &mut [i64]) {
105        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
106        assert!(
107            data.len() >= self.n,
108            "invalid data: data.len()={} < self.n()={}",
109            data.len(),
110            self.n
111        );
112        data.copy_from_slice(self.at(0));
113        let rem: usize = log_base2k - (log_k % log_base2k);
114        (1..limbs).for_each(|i| {
115            if i == limbs - 1 && rem != log_base2k {
116                let k_rem: usize = log_base2k - rem;
117                izip!(self.at(i).iter(), data.iter_mut()).for_each(|(x, y)| {
118                    *y = (*y << k_rem) + (x >> rem);
119                });
120            } else {
121                izip!(self.at(i).iter(), data.iter_mut()).for_each(|(x, y)| {
122                    *y = (*y << log_base2k) + x;
123                });
124            }
125        })
126    }
127
128    fn encode_coeff_i64(
129        &mut self,
130        log_base2k: usize,
131        log_k: usize,
132        i: usize,
133        value: i64,
134        log_max: usize,
135    ) {
136        assert!(i < self.n());
137        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
138        assert!(limbs <= self.limbs(), "invalid argument log_k: (log_k + self.log_base2k - 1)/self.log_base2k={} > self.limbs()={}", limbs, self.limbs());
139        let log_k_rem: usize = log_base2k - (log_k % log_base2k);
140        let limbs = self.limbs();
141
142        // If 2^{log_base2k} * 2^{log_k_rem} < 2^{63}-1, then we can simply copy
143        // values on the last limb.
144        // Else we decompose values base2k.
145        if log_max + log_k_rem < 63 || log_k_rem == log_base2k {
146            (0..limbs - 1).for_each(|j| self.at_mut(j)[i] = 0);
147
148            self.at_mut(self.limbs() - 1)[i] = value;
149        } else {
150            let mask: i64 = (1 << log_base2k) - 1;
151            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
152
153            (0..limbs - steps).for_each(|j| self.at_mut(j)[i] = 0);
154
155            (limbs - steps..limbs)
156                .rev()
157                .enumerate()
158                .for_each(|(j, j_rev)| {
159                    self.at_mut(j_rev)[i] = (value >> (j * log_base2k)) & mask;
160                })
161        }
162
163        // Case where self.prec % self.k != 0.
164        if log_k_rem != log_base2k {
165            let limbs = self.limbs();
166            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
167            (limbs - steps..limbs).rev().for_each(|j| {
168                self.at_mut(j)[i] <<= log_k_rem;
169            })
170        }
171    }
172
173    fn decode_coeff_i64(&self, log_base2k: usize, log_k: usize, i: usize) -> i64 {
174        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
175        assert!(i < self.n());
176        let mut res: i64 = self.data[i];
177        let rem: usize = log_base2k - (log_k % log_base2k);
178        (1..limbs).for_each(|i| {
179            let x = self.data[i * self.n];
180            if i == limbs - 1 && rem != log_base2k {
181                let k_rem: usize = log_base2k - rem;
182                res = (res << k_rem) + (x >> rem);
183            } else {
184                res = (res << log_base2k) + x;
185            }
186        });
187        res
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use crate::{Encoding, VecZnx};
194    use itertools::izip;
195    use sampling::source::Source;
196
197    #[test]
198    fn test_set_get_i64_lo_norm() {
199        let n: usize = 8;
200        let log_base2k: usize = 17;
201        let limbs: usize = 5;
202        let log_k: usize = limbs * log_base2k - 5;
203        let mut a: VecZnx = VecZnx::new(n, limbs);
204        let mut have: Vec<i64> = vec![i64::default(); n];
205        have.iter_mut()
206            .enumerate()
207            .for_each(|(i, x)| *x = (i as i64) - (n as i64) / 2);
208        a.encode_vec_i64(log_base2k, log_k, &have, 10);
209        let mut want = vec![i64::default(); n];
210        a.decode_vec_i64(log_base2k, log_k, &mut want);
211        izip!(want, have).for_each(|(a, b)| assert_eq!(a, b));
212    }
213
214    #[test]
215    fn test_set_get_i64_hi_norm() {
216        let n: usize = 8;
217        let log_base2k: usize = 17;
218        let limbs: usize = 5;
219        let log_k: usize = limbs * log_base2k - 5;
220        let mut a: VecZnx = VecZnx::new(n, limbs);
221        let mut have: Vec<i64> = vec![i64::default(); n];
222        let mut source = Source::new([1; 32]);
223        have.iter_mut().for_each(|x| {
224            *x = source
225                .next_u64n(u64::MAX, u64::MAX)
226                .wrapping_sub(u64::MAX / 2 + 1) as i64;
227        });
228        a.encode_vec_i64(log_base2k, log_k, &have, 63);
229        //(0..a.limbs()).for_each(|i| println!("i:{} -> {:?}", i, a.at(i)));
230        let mut want = vec![i64::default(); n];
231        //(0..a.limbs()).for_each(|i| println!("i:{} -> {:?}", i, a.at(i)));
232        a.decode_vec_i64(log_base2k, log_k, &mut want);
233        izip!(want, have).for_each(|(a, b)| assert_eq!(a, b, "{} != {}", a, b));
234    }
235}