arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/vec_znx.rs
base2k/src/vec_znx.rsRaw
  1use crate::cast_mut_u8_to_mut_i64_slice;
  2use crate::ffi::znx::{
  3    znx_automorphism_i64, znx_automorphism_inplace_i64, znx_normalize, znx_zero_i64_ref,
  4};
  5use crate::module::Module;
  6use itertools::izip;
  7use rand_distr::{Distribution, Normal};
  8use sampling::source::Source;
  9use std::cmp::min;
 10
 11impl Module {
 12    pub fn new_vec_znx(&self, limbs: usize) -> VecZnx {
 13        VecZnx::new(self.n(), limbs)
 14    }
 15}
 16
 17#[derive(Clone)]
 18pub struct VecZnx {
 19    pub n: usize,
 20    pub data: Vec<i64>,
 21}
 22
 23impl VecZnx {
 24    pub fn new(n: usize, limbs: usize) -> Self {
 25        Self {
 26            n: n,
 27            data: vec![i64::default(); Self::buffer_size(n, limbs)],
 28        }
 29    }
 30
 31    pub fn buffer_size(n: usize, limbs: usize) -> usize {
 32        n * limbs
 33    }
 34
 35    pub fn from_buffer(&mut self, n: usize, limbs: usize, buf: &[i64]) {
 36        let size = Self::buffer_size(n, limbs);
 37        assert!(
 38            buf.len() >= size,
 39            "invalid buffer: buf.len()={} < self.buffer_size(n={}, limbs={})={}",
 40            buf.len(),
 41            n,
 42            limbs,
 43            size
 44        );
 45        self.n = n;
 46        self.data = Vec::from(&buf[..size])
 47    }
 48
 49    pub fn log_n(&self) -> u64 {
 50        (u64::BITS - (self.n - 1).leading_zeros()) as _
 51    }
 52
 53    pub fn n(&self) -> usize {
 54        self.n
 55    }
 56
 57    pub fn limbs(&self) -> usize {
 58        self.data.len() / self.n
 59    }
 60
 61    pub fn copy_from(&mut self, a: &VecZnx) {
 62        let size = min(self.data.len(), a.data.len());
 63        self.data[..size].copy_from_slice(&a.data[..size])
 64    }
 65
 66    pub fn as_ptr(&self) -> *const i64 {
 67        self.data.as_ptr()
 68    }
 69
 70    pub fn as_mut_ptr(&mut self) -> *mut i64 {
 71        self.data.as_mut_ptr()
 72    }
 73
 74    pub fn at(&self, i: usize) -> &[i64] {
 75        &self.data[i * self.n..(i + 1) * self.n]
 76    }
 77
 78    pub fn at_ptr(&self, i: usize) -> *const i64 {
 79        &self.data[i * self.n] as *const i64
 80    }
 81
 82    pub fn at_mut_ptr(&mut self, i: usize) -> *mut i64 {
 83        &mut self.data[i * self.n] as *mut i64
 84    }
 85
 86    pub fn at_mut(&mut self, i: usize) -> &mut [i64] {
 87        &mut self.data[i * self.n..(i + 1) * self.n]
 88    }
 89
 90    pub fn zero(&mut self) {
 91        unsafe { znx_zero_i64_ref(self.data.len() as u64, self.data.as_mut_ptr()) }
 92    }
 93
 94    pub fn from_i64(&mut self, log_base2k: usize, data: &[i64], log_max: usize, log_k: usize) {
 95        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
 96
 97        assert!(limbs <= self.limbs(), "invalid argument log_k: (log_k + self.log_base2k - 1)/self.log_base2k={} > self.limbs()={}", limbs, self.limbs());
 98
 99        let size: usize = min(data.len(), self.n());
100        let log_k_rem: usize = log_base2k - (log_k % log_base2k);
101
102        // If 2^{log_base2k} * 2^{k_rem} < 2^{63}-1, then we can simply copy
103        // values on the last limb.
104        // Else we decompose values base2k.
105        if log_max + log_k_rem < 63 || log_k_rem == log_base2k {
106            (0..limbs - 1).for_each(|i| unsafe {
107                znx_zero_i64_ref(size as u64, self.at_mut(i).as_mut_ptr());
108            });
109            self.at_mut(self.limbs() - 1)[..size].copy_from_slice(&data[..size]);
110        } else {
111            let mask: i64 = (1 << log_base2k) - 1;
112            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
113
114            (0..steps).for_each(|i| unsafe {
115                znx_zero_i64_ref(size as u64, self.at_mut(i).as_mut_ptr());
116            });
117
118            (limbs - steps..limbs)
119                .rev()
120                .enumerate()
121                .for_each(|(i, i_rev)| {
122                    let shift: usize = i * log_base2k;
123                    izip!(self.at_mut(i_rev)[..size].iter_mut(), data[..size].iter())
124                        .for_each(|(y, x)| *y = (x >> shift) & mask);
125                })
126        }
127
128        // Case where self.prec % self.k != 0.
129        if log_k_rem != log_base2k {
130            let limbs = self.limbs();
131            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
132            (limbs - steps..limbs).rev().for_each(|i| {
133                self.at_mut(i)[..size]
134                    .iter_mut()
135                    .for_each(|x| *x <<= log_k_rem);
136            })
137        }
138    }
139
140    pub fn from_i64_single(
141        &mut self,
142        log_base2k: usize,
143        i: usize,
144        value: i64,
145        log_max: usize,
146        log_k: usize,
147    ) {
148        assert!(i < self.n());
149        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
150        assert!(limbs <= self.limbs(), "invalid argument log_k: (log_k + self.log_base2k - 1)/self.log_base2k={} > self.limbs()={}", limbs, self.limbs());
151        let log_k_rem: usize = log_base2k - (log_k % log_base2k);
152        let limbs = self.limbs();
153
154        // If 2^{log_base2k} * 2^{log_k_rem} < 2^{63}-1, then we can simply copy
155        // values on the last limb.
156        // Else we decompose values base2k.
157        if log_max + log_k_rem < 63 || log_k_rem == log_base2k {
158            (0..limbs - 1).for_each(|j| self.at_mut(j)[i] = 0);
159
160            self.at_mut(self.limbs() - 1)[i] = value;
161        } else {
162            let mask: i64 = (1 << log_base2k) - 1;
163            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
164
165            (0..limbs - steps).for_each(|j| self.at_mut(j)[i] = 0);
166
167            (limbs - steps..limbs)
168                .rev()
169                .enumerate()
170                .for_each(|(j, j_rev)| {
171                    self.at_mut(j_rev)[i] = (value >> (j * log_base2k)) & mask;
172                })
173        }
174
175        // Case where self.prec % self.k != 0.
176        if log_k_rem != log_base2k {
177            let limbs = self.limbs();
178            let steps: usize = min(limbs, (log_max + log_base2k - 1) / log_base2k);
179            (limbs - steps..limbs).rev().for_each(|j| {
180                self.at_mut(j)[i] <<= log_k_rem;
181            })
182        }
183    }
184
185    pub fn normalize(&mut self, log_base2k: usize, carry: &mut [u8]) {
186        assert!(
187            carry.len() >= self.n * 8,
188            "invalid carry: carry.len()={} < self.n()={}",
189            carry.len(),
190            self.n()
191        );
192
193        let carry_i64: &mut [i64] = cast_mut_u8_to_mut_i64_slice(carry);
194
195        unsafe {
196            znx_zero_i64_ref(self.n() as u64, carry_i64.as_mut_ptr());
197            (0..self.limbs()).rev().for_each(|i| {
198                znx_normalize(
199                    self.n as u64,
200                    log_base2k as u64,
201                    self.at_mut_ptr(i),
202                    carry_i64.as_mut_ptr(),
203                    self.at_mut_ptr(i),
204                    carry_i64.as_mut_ptr(),
205                )
206            });
207        }
208    }
209
210    pub fn to_i64(&self, log_base2k: usize, data: &mut [i64], log_k: usize) {
211        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
212        assert!(
213            data.len() >= self.n,
214            "invalid data: data.len()={} < self.n()={}",
215            data.len(),
216            self.n
217        );
218        data.copy_from_slice(self.at(0));
219        let rem: usize = log_base2k - (log_k % log_base2k);
220        (1..limbs).for_each(|i| {
221            if i == limbs - 1 && rem != log_base2k {
222                let k_rem: usize = log_base2k - rem;
223                izip!(self.at(i).iter(), data.iter_mut()).for_each(|(x, y)| {
224                    *y = (*y << k_rem) + (x >> rem);
225                });
226            } else {
227                izip!(self.at(i).iter(), data.iter_mut()).for_each(|(x, y)| {
228                    *y = (*y << log_base2k) + x;
229                });
230            }
231        })
232    }
233
234    pub fn to_i64_single(&self, log_base2k: usize, i: usize, log_k: usize) -> i64 {
235        let limbs: usize = (log_k + log_base2k - 1) / log_base2k;
236        assert!(i < self.n());
237        let mut res: i64 = self.data[i];
238        let rem: usize = log_base2k - (log_k % log_base2k);
239        (1..limbs).for_each(|i| {
240            let x = self.data[i * self.n];
241            if i == limbs - 1 && rem != log_base2k {
242                let k_rem: usize = log_base2k - rem;
243                res = (res << k_rem) + (x >> rem);
244            } else {
245                res = (res << log_base2k) + x;
246            }
247        });
248        res
249    }
250
251    pub fn automorphism_inplace(&mut self, gal_el: i64) {
252        unsafe {
253            (0..self.limbs()).for_each(|i| {
254                znx_automorphism_inplace_i64(self.n as u64, gal_el, self.at_mut_ptr(i))
255            })
256        }
257    }
258    pub fn automorphism(&mut self, gal_el: i64, a: &mut VecZnx) {
259        unsafe {
260            (0..self.limbs()).for_each(|i| {
261                znx_automorphism_i64(self.n as u64, gal_el, a.at_mut_ptr(i), self.at_ptr(i))
262            })
263        }
264    }
265
266    pub fn fill_uniform(&mut self, log_base2k: usize, source: &mut Source, limbs: usize) {
267        let base2k: u64 = 1 << log_base2k;
268        let mask: u64 = base2k - 1;
269        let base2k_half: i64 = (base2k >> 1) as i64;
270
271        let size: usize = self.n() * (limbs - 1);
272
273        self.data[..size]
274            .iter_mut()
275            .for_each(|x| *x = (source.next_u64n(base2k, mask) as i64) - base2k_half);
276    }
277
278    pub fn add_dist_f64<T: Distribution<f64>>(
279        &mut self,
280        log_base2k: usize,
281        source: &mut Source,
282        dist: T,
283        bound: f64,
284        log_k: usize,
285    ) {
286        let log_base2k_rem: usize = log_k % log_base2k;
287
288        if log_base2k_rem != 0 {
289            self.at_mut(self.limbs() - 1).iter_mut().for_each(|a| {
290                let mut dist_f64: f64 = dist.sample(source);
291                while dist_f64.abs() > bound {
292                    dist_f64 = dist.sample(source)
293                }
294                *a += (dist_f64.round() as i64) << log_base2k_rem
295            });
296        } else {
297            self.at_mut(self.limbs() - 1).iter_mut().for_each(|a| {
298                let mut dist_f64: f64 = dist.sample(source);
299                while dist_f64.abs() > bound {
300                    dist_f64 = dist.sample(source)
301                }
302                *a += dist_f64.round() as i64
303            });
304        }
305    }
306
307    pub fn add_normal(
308        &mut self,
309        log_base2k: usize,
310        source: &mut Source,
311        sigma: f64,
312        bound: f64,
313        log_k: usize,
314    ) {
315        self.add_dist_f64(
316            log_base2k,
317            source,
318            Normal::new(0.0, sigma).unwrap(),
319            bound,
320            log_k,
321        );
322    }
323
324    pub fn trunc_pow2(&mut self, log_base2k: usize, k: usize) {
325        if k == 0 {
326            return;
327        }
328
329        self.data
330            .truncate((self.limbs() - k / log_base2k) * self.n());
331
332        let k_rem: usize = k % log_base2k;
333
334        if k_rem != 0 {
335            let mask: i64 = ((1 << (log_base2k - k_rem - 1)) - 1) << k_rem;
336            self.at_mut(self.limbs() - 1)
337                .iter_mut()
338                .for_each(|x: &mut i64| *x &= mask)
339        }
340    }
341
342    pub fn rsh(&mut self, log_base2k: usize, k: usize, carry: &mut [u8]) {
343        assert!(
344            carry.len() >> 3 >= self.n(),
345            "invalid carry: carry.len()/8={} < self.n()={}",
346            carry.len() >> 3,
347            self.n()
348        );
349
350        let limbs: usize = self.limbs();
351        let limbs_steps: usize = k / log_base2k;
352
353        self.data.rotate_right(self.n * limbs_steps);
354        unsafe {
355            znx_zero_i64_ref((self.n * limbs_steps) as u64, self.data.as_mut_ptr());
356        }
357
358        let k_rem = k % log_base2k;
359
360        if k_rem != 0 {
361            let carry_i64: &mut [i64] = cast_mut_u8_to_mut_i64_slice(carry);
362
363            unsafe {
364                znx_zero_i64_ref(self.n() as u64, carry_i64.as_mut_ptr());
365            }
366
367            let mask: i64 = (1 << k_rem) - 1;
368            let log_base2k: usize = log_base2k;
369
370            (limbs_steps..limbs).for_each(|i| {
371                izip!(carry_i64.iter_mut(), self.at_mut(i).iter_mut()).for_each(|(ci, xi)| {
372                    *xi += *ci << log_base2k;
373                    *ci = *xi & mask;
374                    *xi /= 1 << k_rem;
375                });
376            })
377        }
378    }
379
380    pub fn switch_degree(&self, a: &mut VecZnx) {
381        let (n_in, n_out) = (self.n(), a.n());
382        let (gap_in, gap_out): (usize, usize);
383
384        if n_in > n_out {
385            (gap_in, gap_out) = (n_in / n_out, 1)
386        } else {
387            (gap_in, gap_out) = (1, n_out / n_in);
388            a.zero();
389        }
390
391        let limbs = min(self.limbs(), a.limbs());
392
393        (0..limbs).for_each(|i| {
394            izip!(
395                self.at(i).iter().step_by(gap_in),
396                a.at_mut(i).iter_mut().step_by(gap_out)
397            )
398            .for_each(|(x_in, x_out)| *x_out = *x_in);
399        });
400    }
401
402    pub fn print_limbs(&self, limbs: usize, n: usize) {
403        (0..limbs).for_each(|i| println!("{}: {:?}", i, &self.at(i)[..n]))
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use crate::VecZnx;
410    use itertools::izip;
411    use sampling::source::Source;
412
413    #[test]
414    fn test_set_get_i64_lo_norm() {
415        let n: usize = 8;
416        let log_base2k: usize = 17;
417        let limbs: usize = 5;
418        let log_k: usize = limbs * log_base2k - 5;
419        let mut a: VecZnx = VecZnx::new(n, limbs);
420        let mut have: Vec<i64> = vec![i64::default(); n];
421        have.iter_mut()
422            .enumerate()
423            .for_each(|(i, x)| *x = (i as i64) - (n as i64) / 2);
424        a.from_i64(log_base2k, &have, 10, log_k);
425        let mut want = vec![i64::default(); n];
426        a.to_i64(log_base2k, &mut want, log_k);
427        izip!(want, have).for_each(|(a, b)| assert_eq!(a, b));
428    }
429
430    #[test]
431    fn test_set_get_i64_hi_norm() {
432        let n: usize = 8;
433        let log_base2k: usize = 17;
434        let limbs: usize = 5;
435        let log_k: usize = limbs * log_base2k - 5;
436        let mut a: VecZnx = VecZnx::new(n, limbs);
437        let mut have: Vec<i64> = vec![i64::default(); n];
438        let mut source = Source::new([1; 32]);
439        have.iter_mut().for_each(|x| {
440            *x = source
441                .next_u64n(u64::MAX, u64::MAX)
442                .wrapping_sub(u64::MAX / 2 + 1) as i64;
443        });
444        a.from_i64(log_base2k, &have, 63, log_k);
445        //(0..a.limbs()).for_each(|i| println!("i:{} -> {:?}", i, a.at(i)));
446        let mut want = vec![i64::default(); n];
447        //(0..a.limbs()).for_each(|i| println!("i:{} -> {:?}", i, a.at(i)));
448        a.to_i64(log_base2k, &mut want, log_k);
449        izip!(want, have).for_each(|(a, b)| assert_eq!(a, b, "{} != {}", a, b));
450    }
451    #[test]
452    fn test_normalize() {
453        let n: usize = 8;
454        let log_base2k: usize = 17;
455        let limbs: usize = 5;
456        let log_k: usize = limbs * log_base2k - 5;
457        let mut a: VecZnx = VecZnx::new(n, limbs);
458        let mut have: Vec<i64> = vec![i64::default(); n];
459        let mut source = Source::new([1; 32]);
460        have.iter_mut().for_each(|x| {
461            *x = source
462                .next_u64n(u64::MAX, u64::MAX)
463                .wrapping_sub(u64::MAX / 2 + 1) as i64;
464        });
465        a.from_i64(log_base2k, &have, 63, log_k);
466        let mut carry: Vec<u8> = vec![u8::default(); n * 8];
467        a.normalize(log_base2k, &mut carry);
468
469        let base_half = 1 << (log_base2k - 1);
470        a.data
471            .iter()
472            .for_each(|x| assert!(x.abs() <= base_half, "|x|={} > 2^(k-1)={}", x, base_half));
473        let mut want = vec![i64::default(); n];
474        a.to_i64(log_base2k, &mut want, log_k);
475        izip!(want, have).for_each(|(a, b)| assert_eq!(a, b, "{} != {}", a, b));
476    }
477}