1use std::fmt;
2
3use poulpy_hal::{
4 layouts::{Backend, Data, FillUniform, HostDataMut, HostDataRef, ReaderFrom, WriterTo},
5 source::Source,
6};
7
8use crate::{
9 DeclaredK,
10 layouts::{
11 Base2K, Degree, Dnum, Dsize, GGLWEAtViewMut, GGLWEAtViewRef, GGLWEBackendMut, GGLWEBackendRef, GGLWEInfos,
12 GGLWEToBackendMut, GGLWEToBackendRef, GLWEInfos, GLWESwitchingKey, GLWESwitchingKeyDegrees, GLWESwitchingKeyDegreesMut,
13 GLWEViewMut, GLWEViewRef, LWEInfos, Rank, TorusPrecision,
14 },
15};
16
17#[derive(PartialEq, Eq, Copy, Clone, Debug)]
18pub struct LWEToGLWEKeyLayout {
19 pub n: Degree,
20 pub base2k: Base2K,
21 pub k: TorusPrecision,
22 pub rank_out: Rank,
23 pub dnum: Dnum,
24}
25
26impl DeclaredK for LWEToGLWEKeyLayout {
27 fn k(&self) -> TorusPrecision {
28 self.k
29 }
30}
31
32impl LWEInfos for LWEToGLWEKeyLayout {
33 fn base2k(&self) -> Base2K {
34 self.base2k
35 }
36
37 fn n(&self) -> Degree {
38 self.n
39 }
40
41 fn size(&self) -> usize {
42 self.k.as_usize().div_ceil(self.base2k.as_usize())
43 }
44}
45
46impl GLWEInfos for LWEToGLWEKeyLayout {
47 fn rank(&self) -> Rank {
48 self.rank_out()
49 }
50}
51
52impl GGLWEInfos for LWEToGLWEKeyLayout {
53 fn rank_in(&self) -> Rank {
54 Rank(1)
55 }
56
57 fn dsize(&self) -> Dsize {
58 Dsize(1)
59 }
60
61 fn rank_out(&self) -> Rank {
62 self.rank_out
63 }
64
65 fn dnum(&self) -> Dnum {
66 self.dnum
67 }
68}
69
70#[derive(PartialEq, Eq, Clone)]
71pub struct LWEToGLWEKey<D: Data>(pub(crate) GLWESwitchingKey<D>);
72
73impl<D: Data> LWEInfos for LWEToGLWEKey<D> {
74 fn base2k(&self) -> Base2K {
75 self.0.base2k()
76 }
77
78 fn n(&self) -> Degree {
79 self.0.n()
80 }
81
82 fn size(&self) -> usize {
83 self.0.size()
84 }
85}
86
87impl<D: Data> GLWEInfos for LWEToGLWEKey<D> {
88 fn rank(&self) -> Rank {
89 self.rank_out()
90 }
91}
92impl<D: Data> GGLWEInfos for LWEToGLWEKey<D> {
93 fn dsize(&self) -> Dsize {
94 self.0.dsize()
95 }
96
97 fn rank_in(&self) -> Rank {
98 self.0.rank_in()
99 }
100
101 fn rank_out(&self) -> Rank {
102 self.0.rank_out()
103 }
104
105 fn dnum(&self) -> Dnum {
106 self.0.dnum()
107 }
108}
109
110impl<D: HostDataRef> fmt::Debug for LWEToGLWEKey<D> {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 write!(f, "{self}")
113 }
114}
115
116impl<D: HostDataMut> FillUniform for LWEToGLWEKey<D> {
117 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
118 self.0.fill_uniform(log_bound, source);
119 }
120}
121
122impl<D: HostDataRef> fmt::Display for LWEToGLWEKey<D> {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "(LWEToGLWEKey) {}", self.0)
125 }
126}
127
128impl<D: HostDataMut> ReaderFrom for LWEToGLWEKey<D> {
129 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
130 self.0.read_from(reader)
131 }
132}
133
134impl<D: HostDataRef> WriterTo for LWEToGLWEKey<D> {
135 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
136 self.0.write_to(writer)
137 }
138}
139
140#[expect(
141 dead_code,
142 reason = "host-owned constructors are kept for serialization and host-only staging"
143)]
144impl LWEToGLWEKey<Vec<u8>> {
145 pub(crate) fn alloc_from_infos<A>(infos: &A) -> Self
146 where
147 A: GGLWEInfos,
148 {
149 assert_eq!(infos.rank_in().0, 1, "rank_in > 1 is not supported for LWEToGLWEKey");
150 assert_eq!(infos.dsize().0, 1, "dsize > 1 is not supported for LWEToGLWEKey");
151
152 Self::alloc(infos.n(), infos.base2k(), infos.max_k(), infos.rank_out(), infos.dnum())
153 }
154
155 pub(crate) fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank_out: Rank, dnum: Dnum) -> Self {
156 LWEToGLWEKey(GLWESwitchingKey::alloc(n, base2k, k, Rank(1), rank_out, dnum, Dsize(1)))
157 }
158
159 pub fn bytes_of_from_infos<A>(infos: &A) -> usize
160 where
161 A: GGLWEInfos,
162 {
163 assert_eq!(infos.rank_in().0, 1, "rank_in > 1 is not supported for LWEToGLWEKey");
164 assert_eq!(infos.dsize().0, 1, "dsize > 1 is not supported for LWEToGLWEKey");
165 Self::bytes_of(infos.n(), infos.base2k(), infos.max_k(), infos.rank_out(), infos.dnum())
166 }
167
168 pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank_out: Rank, dnum: Dnum) -> usize {
169 GLWESwitchingKey::bytes_of(n, base2k, k, Rank(1), rank_out, dnum, Dsize(1))
170 }
171}
172
173impl_gglwe_to_backend_for_field!(LWEToGLWEKey<D>, 0, GLWESwitchingKey<D>);
174
175impl_gglwe_at_view_for_field!(LWEToGLWEKey<BE::OwnedBuf>; 0.key);
176
177impl<D: HostDataMut> GLWESwitchingKeyDegreesMut for LWEToGLWEKey<D> {
178 fn input_degree(&mut self) -> &mut Degree {
179 &mut self.0.input_degree
180 }
181
182 fn output_degree(&mut self) -> &mut Degree {
183 &mut self.0.output_degree
184 }
185}
186
187impl<D: HostDataRef> GLWESwitchingKeyDegrees for LWEToGLWEKey<D> {
188 fn input_degree(&self) -> &Degree {
189 &self.0.input_degree
190 }
191
192 fn output_degree(&self) -> &Degree {
193 &self.0.output_degree
194 }
195}