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