poulpy/base2k/src/svp.rs
base2k/src/svp.rsRaw
1use crate::ffi::svp;
2use crate::{Module, VecZnx, VecZnxDft};
3
4use crate::Infos;
5use rand::seq::SliceRandom;
6use rand_core::RngCore;
7use rand_distr::{Distribution, WeightedIndex};
8use sampling::source::Source;
9
10pub struct Scalar(pub Vec<i64>);
11
12impl Module {
13 pub fn new_scalar(&self) -> Scalar {
14 Scalar::new(self.n())
15 }
16}
17
18impl Scalar {
19 pub fn new(n: usize) -> Self {
20 Self(vec![i64::default(); Self::buffer_size(n)])
21 }
22
23 pub fn buffer_size(n: usize) -> usize {
24 n
25 }
26
27 pub fn from_buffer(&mut self, n: usize, buf: &[i64]) {
28 let size: usize = Self::buffer_size(n);
29 assert!(
30 buf.len() >= size,
31 "invalid buffer: buf.len()={} < self.buffer_size(n={})={}",
32 buf.len(),
33 n,
34 size
35 );
36 self.0 = Vec::from(&buf[..size])
37 }
38
39 pub fn as_ptr(&self) -> *const i64 {
40 self.0.as_ptr()
41 }
42
43 pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
44 let choices: [i64; 3] = [-1, 0, 1];
45 let weights: [f64; 3] = [prob / 2.0, 1.0 - prob, prob / 2.0];
46 let dist: WeightedIndex<f64> = WeightedIndex::new(&weights).unwrap();
47 self.0
48 .iter_mut()
49 .for_each(|x: &mut i64| *x = choices[dist.sample(source)]);
50 }
51
52 pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
53 self.0[..hw]
54 .iter_mut()
55 .for_each(|x: &mut i64| *x = (((source.next_u32() & 1) as i64) << 1) - 1);
56 self.0.shuffle(source);
57 }
58}
59
60pub struct SvpPPol(pub *mut svp::svp_ppol_t, pub usize);
61
62/// A prepared [crate::Scalar] for [SvpPPolOps::svp_apply_dft].
63/// An [SvpPPol] an be seen as a [VecZnxDft] of one limb.
64/// The backend array of an [SvpPPol] is allocated in C and must be freed manually.
65impl SvpPPol {
66 /// Returns the ring degree of the [SvpPPol].
67 pub fn n(&self) -> usize {
68 self.1
69 }
70
71 /// Returns the number of limbs of the [SvpPPol], which is always 1.
72 pub fn limbs(&self) -> usize {
73 1
74 }
75}
76
77pub trait SvpPPolOps {
78 /// Prepares a [crate::Scalar] for a [SvpPPolOps::svp_apply_dft].
79 fn svp_prepare(&self, svp_ppol: &mut SvpPPol, a: &Scalar);
80
81 /// Allocates a new [SvpPPol].
82 fn svp_new_ppol(&self) -> SvpPPol;
83
84 /// Applies the [SvpPPol] x [VecZnxDft] product, where each limb of
85 /// the [VecZnxDft] is multiplied with [SvpPPol].
86 fn svp_apply_dft(&self, c: &mut VecZnxDft, a: &SvpPPol, b: &VecZnx);
87}
88
89impl SvpPPolOps for Module {
90 fn svp_prepare(&self, svp_ppol: &mut SvpPPol, a: &Scalar) {
91 unsafe { svp::svp_prepare(self.0, svp_ppol.0, a.as_ptr()) }
92 }
93
94 fn svp_new_ppol(&self) -> SvpPPol {
95 unsafe { SvpPPol(svp::new_svp_ppol(self.0), self.n()) }
96 }
97
98 fn svp_apply_dft(&self, c: &mut VecZnxDft, a: &SvpPPol, b: &VecZnx) {
99 let limbs: u64 = b.limbs() as u64;
100 assert!(
101 c.limbs() as u64 >= limbs,
102 "invalid c_vector: c_vector.limbs()={} < b.limbs()={}",
103 c.limbs(),
104 limbs
105 );
106 unsafe { svp::svp_apply_dft(self.0, c.0, limbs, a.0, b.as_ptr(), limbs, b.n() as u64) }
107 }
108}