poulpy/base2k/src/svp.rs
base2k/src/svp.rsRaw
1use crate::ffi::svp;
2use crate::{alias_mut_slice_to_vec, Module, VecZnxApi, VecZnxDft};
3
4use crate::{alloc_aligned, cast, 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(alloc_aligned::<i64>(n, 64))
21 }
22
23 pub fn n(&self) -> usize {
24 self.0.len()
25 }
26
27 pub fn buffer_size(n: usize) -> usize {
28 n
29 }
30
31 pub fn from_buffer(&mut self, n: usize, buf: &mut [u8]) {
32 let size: usize = Self::buffer_size(n);
33 assert!(
34 buf.len() >= size,
35 "invalid buffer: buf.len()={} < self.buffer_size(n={})={}",
36 buf.len(),
37 n,
38 size
39 );
40 self.0 = alias_mut_slice_to_vec(cast::<u8, i64>(&buf[..size]))
41 }
42
43 pub fn as_ptr(&self) -> *const i64 {
44 self.0.as_ptr()
45 }
46
47 pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
48 let choices: [i64; 3] = [-1, 0, 1];
49 let weights: [f64; 3] = [prob / 2.0, 1.0 - prob, prob / 2.0];
50 let dist: WeightedIndex<f64> = WeightedIndex::new(&weights).unwrap();
51 self.0
52 .iter_mut()
53 .for_each(|x: &mut i64| *x = choices[dist.sample(source)]);
54 }
55
56 pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
57 assert!(hw <= self.n());
58 self.0[..hw]
59 .iter_mut()
60 .for_each(|x: &mut i64| *x = (((source.next_u32() & 1) as i64) << 1) - 1);
61 self.0.shuffle(source);
62 }
63}
64
65pub struct SvpPPol(pub *mut svp::svp_ppol_t, pub usize);
66
67/// A prepared [crate::Scalar] for [SvpPPolOps::svp_apply_dft].
68/// An [SvpPPol] an be seen as a [VecZnxDft] of one limb.
69/// The backend array of an [SvpPPol] is allocated in C and must be freed manually.
70impl SvpPPol {
71 /// Returns the ring degree of the [SvpPPol].
72 pub fn n(&self) -> usize {
73 self.1
74 }
75
76 pub fn from_bytes(size: usize, bytes: &mut [u8]) -> SvpPPol {
77 SvpPPol(bytes.as_mut_ptr() as *mut svp::svp_ppol_t, size)
78 }
79
80 /// Returns the number of limbs of the [SvpPPol], which is always 1.
81 pub fn limbs(&self) -> usize {
82 1
83 }
84}
85
86pub trait SvpPPolOps {
87 /// Allocates a new [SvpPPol].
88 fn new_svp_ppol(&self) -> SvpPPol;
89
90 /// Returns the minimum number of bytes necessary to allocate
91 /// a new [SvpPPol] through [SvpPPol::from_bytes].
92 fn bytes_of_svp_ppol(&self) -> usize;
93
94 /// Prepares a [crate::Scalar] for a [SvpPPolOps::svp_apply_dft].
95 fn svp_prepare(&self, svp_ppol: &mut SvpPPol, a: &Scalar);
96
97 /// Applies the [SvpPPol] x [VecZnxDft] product, where each limb of
98 /// the [VecZnxDft] is multiplied with [SvpPPol].
99 fn svp_apply_dft<T: VecZnxApi + Infos>(
100 &self,
101 c: &mut VecZnxDft,
102 a: &SvpPPol,
103 b: &T,
104 b_limbs: usize,
105 );
106}
107
108impl SvpPPolOps for Module {
109 fn new_svp_ppol(&self) -> SvpPPol {
110 unsafe { SvpPPol(svp::new_svp_ppol(self.0), self.n()) }
111 }
112
113 fn bytes_of_svp_ppol(&self) -> usize {
114 unsafe { svp::bytes_of_svp_ppol(self.0) as usize }
115 }
116
117 fn svp_prepare(&self, svp_ppol: &mut SvpPPol, a: &Scalar) {
118 unsafe { svp::svp_prepare(self.0, svp_ppol.0, a.as_ptr()) }
119 }
120
121 fn svp_apply_dft<T: VecZnxApi + Infos>(
122 &self,
123 c: &mut VecZnxDft,
124 a: &SvpPPol,
125 b: &T,
126 b_limbs: usize,
127 ) {
128 assert!(
129 c.limbs() >= b_limbs,
130 "invalid c_vector: c_vector.limbs()={} < b.limbs()={}",
131 c.limbs(),
132 b_limbs
133 );
134 unsafe {
135 svp::svp_apply_dft(
136 self.0,
137 c.0,
138 b_limbs as u64,
139 a.0,
140 b.as_ptr(),
141 b_limbs as u64,
142 b.n() as u64,
143 )
144 }
145 }
146}