Skip to main content

poulpy_core/layouts/
ggsw.rs

1use poulpy_hal::{
2    layouts::{
3        Backend, Data, FillUniform, HostDataMut, HostDataRef, MatZnx, MatZnxAtBackendMut, MatZnxAtBackendRef, MatZnxToBackendMut,
4        MatZnxToBackendRef, ReaderFrom, WriterTo,
5    },
6    source::Source,
7};
8use std::{
9    fmt,
10    ops::{Deref, DerefMut},
11};
12
13use crate::layouts::{Base2K, Degree, Dnum, Dsize, GLWE, GLWEInfos, GLWEViewMut, GLWEViewRef, LWEInfos, Rank, TorusPrecision};
14use poulpy_hal::layouts::ZnxWord;
15
16/// Trait providing the parameter accessors for a GGSW (Gadget GSW) ciphertext.
17///
18/// A GGSW ciphertext is a matrix of [`GLWE`] ciphertexts with `rank_in = rank + 1`
19/// input columns and `rank_out = rank + 1` output columns. It is used as the
20/// left operand of external products.
21/// Extends [`GLWEInfos`] with gadget decomposition parameters.
22pub trait GGSWInfos
23where
24    Self: GLWEInfos,
25{
26    /// Auxiliary guard precision (in bits) stored below the gadget region and
27    /// used for noise management during key operations. Free value, need not be
28    /// a multiple of `base2k`.
29    fn k_aux(&self) -> TorusPrecision;
30    /// Returns the number of gadget-decomposition rows.
31    fn dnum(&self) -> Dnum;
32    /// Returns the decomposition digit size.
33    fn dsize(&self) -> Dsize;
34    /// Returns a plain-data [`GGSWLayout`] snapshot of the current parameters.
35    fn ggsw_layout(&self) -> GGSWLayout {
36        GGSWLayout {
37            n: self.n(),
38            base2k: self.base2k(),
39            dnum: self.dnum(),
40            k_aux: self.k_aux(),
41            rank: self.rank(),
42            dsize: self.dsize(),
43        }
44    }
45}
46
47/// Plain-data snapshot of the parameters that describe a [`GGSW`] ciphertext.
48#[derive(PartialEq, Eq, Copy, Clone, Debug)]
49pub struct GGSWLayout {
50    /// Ring degree.
51    pub n: Degree,
52    /// Base-2-log of the limb width.
53    pub base2k: Base2K,
54    /// Number of gadget-decomposition rows.
55    pub dnum: Dnum,
56    /// Auxiliary guard precision (torus bits) below the gadget region.
57    pub k_aux: TorusPrecision,
58    /// GLWE rank (number of mask polynomials per row).
59    pub rank: Rank,
60    /// Decomposition digit size.
61    pub dsize: Dsize,
62}
63
64impl LWEInfos for GGSWLayout {
65    fn base2k(&self) -> Base2K {
66        self.base2k
67    }
68
69    fn n(&self) -> Degree {
70        self.n
71    }
72
73    fn max_size(&self) -> usize {
74        crate::layouts::key_size(self.base2k, self.dnum, self.dsize, self.k_aux)
75    }
76
77    fn k(&self) -> TorusPrecision {
78        crate::layouts::key_k(self.base2k, self.dnum, self.dsize, self.k_aux)
79    }
80}
81impl GLWEInfos for GGSWLayout {
82    fn rank(&self) -> Rank {
83        self.rank
84    }
85}
86
87impl GGSWInfos for GGSWLayout {
88    fn k_aux(&self) -> TorusPrecision {
89        self.k_aux
90    }
91
92    fn dnum(&self) -> Dnum {
93        self.dnum
94    }
95
96    fn dsize(&self) -> Dsize {
97        self.dsize
98    }
99}
100
101/// A GGSW (Gadget GSW) ciphertext.
102///
103/// Stored as a [`MatZnx`] matrix of [`GLWE`] ciphertexts with
104/// `rank_in = rank + 1` input columns and `rank_out = rank + 1` output columns.
105/// Used as the left operand of external products.
106///
107/// `D: Data` is the storage backend (e.g. `Vec<u8>`, `&[u8]`, `&mut [u8]`).
108#[derive(PartialEq, Eq, Clone)]
109pub struct GGSW<D: Data, W: ZnxWord> {
110    pub(crate) data: MatZnx<D, W>,
111    pub(crate) k_aux: TorusPrecision,
112    pub(crate) base2k: Base2K,
113    pub(crate) dsize: Dsize,
114}
115
116pub struct GGSWBackendRef<'a, BE: Backend + 'a> {
117    inner: GGSW<BE::BufRef<'a>, BE::ZnxWord>,
118}
119
120impl<'a, BE: Backend + 'a> GGSWBackendRef<'a, BE> {
121    pub fn from_inner(inner: GGSW<BE::BufRef<'a>, BE::ZnxWord>) -> Self {
122        Self { inner }
123    }
124
125    pub fn into_inner(self) -> GGSW<BE::BufRef<'a>, BE::ZnxWord> {
126        self.inner
127    }
128
129    pub fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
130        GLWEViewRef::from_inner(ggsw_at_backend_ref_from_ref::<BE>(&self.inner, row, col))
131    }
132}
133
134impl<'a, BE: Backend + 'a> Deref for GGSWBackendRef<'a, BE> {
135    type Target = GGSW<BE::BufRef<'a>, BE::ZnxWord>;
136
137    fn deref(&self) -> &Self::Target {
138        &self.inner
139    }
140}
141
142pub struct GGSWBackendMut<'a, BE: Backend + 'a> {
143    inner: GGSW<BE::BufMut<'a>, BE::ZnxWord>,
144}
145
146impl<'a, BE: Backend + 'a> GGSWBackendMut<'a, BE> {
147    pub fn from_inner(inner: GGSW<BE::BufMut<'a>, BE::ZnxWord>) -> Self {
148        Self { inner }
149    }
150
151    pub fn into_inner(self) -> GGSW<BE::BufMut<'a>, BE::ZnxWord> {
152        self.inner
153    }
154
155    pub fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
156        GLWEViewRef::from_inner(ggsw_at_backend_ref_from_mut::<BE>(&self.inner, row, col))
157    }
158
159    pub fn at_view_mut(&mut self, row: usize, col: usize) -> GLWEViewMut<'_, BE> {
160        GLWEViewMut::from_inner(ggsw_at_backend_mut_from_mut::<BE>(&mut self.inner, row, col))
161    }
162}
163
164impl<'a, BE: Backend + 'a> Deref for GGSWBackendMut<'a, BE> {
165    type Target = GGSW<BE::BufMut<'a>, BE::ZnxWord>;
166
167    fn deref(&self) -> &Self::Target {
168        &self.inner
169    }
170}
171
172impl<'a, BE: Backend + 'a> DerefMut for GGSWBackendMut<'a, BE> {
173    fn deref_mut(&mut self) -> &mut Self::Target {
174        &mut self.inner
175    }
176}
177
178impl<'a, BE: Backend + 'a> LWEInfos for GGSWBackendRef<'a, BE> {
179    fn base2k(&self) -> Base2K {
180        self.inner.base2k()
181    }
182
183    fn n(&self) -> Degree {
184        self.inner.n()
185    }
186
187    fn max_size(&self) -> usize {
188        self.inner.max_size()
189    }
190
191    fn k(&self) -> TorusPrecision {
192        self.inner.k()
193    }
194}
195
196impl<'a, BE: Backend + 'a> GLWEInfos for GGSWBackendRef<'a, BE> {
197    fn rank(&self) -> Rank {
198        self.inner.rank()
199    }
200}
201
202impl<'a, BE: Backend + 'a> GGSWInfos for GGSWBackendRef<'a, BE> {
203    fn k_aux(&self) -> TorusPrecision {
204        self.inner.k_aux()
205    }
206
207    fn dnum(&self) -> Dnum {
208        self.inner.dnum()
209    }
210
211    fn dsize(&self) -> Dsize {
212        self.inner.dsize()
213    }
214}
215
216impl<'a, BE: Backend + 'a> LWEInfos for GGSWBackendMut<'a, BE> {
217    fn base2k(&self) -> Base2K {
218        self.inner.base2k()
219    }
220
221    fn n(&self) -> Degree {
222        self.inner.n()
223    }
224
225    fn max_size(&self) -> usize {
226        self.inner.max_size()
227    }
228
229    fn k(&self) -> TorusPrecision {
230        self.inner.k()
231    }
232}
233
234impl<'a, BE: Backend + 'a> GLWEInfos for GGSWBackendMut<'a, BE> {
235    fn rank(&self) -> Rank {
236        self.inner.rank()
237    }
238}
239
240impl<'a, BE: Backend + 'a> GGSWInfos for GGSWBackendMut<'a, BE> {
241    fn k_aux(&self) -> TorusPrecision {
242        self.inner.k_aux()
243    }
244
245    fn dnum(&self) -> Dnum {
246        self.inner.dnum()
247    }
248
249    fn dsize(&self) -> Dsize {
250        self.inner.dsize()
251    }
252}
253
254impl<'a, BE: Backend + 'a> GGSWToBackendRef<BE> for GGSWBackendRef<'a, BE> {
255    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE> {
256        GGSWBackendRef::from_inner(GGSW {
257            dsize: self.inner.dsize(),
258            base2k: self.inner.base2k(),
259            k_aux: self.inner.k_aux(),
260            data: poulpy_hal::layouts::mat_znx_backend_ref_from_ref::<BE>(&self.inner.data),
261        })
262    }
263}
264
265impl<'a, BE: Backend + 'a> GGSWToBackendRef<BE> for GGSWBackendMut<'a, BE> {
266    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE> {
267        GGSWBackendRef::from_inner(GGSW {
268            dsize: self.inner.dsize,
269            base2k: self.inner.base2k,
270            k_aux: self.inner.k_aux,
271            data: poulpy_hal::layouts::mat_znx_backend_ref_from_mut::<BE>(&self.inner.data),
272        })
273    }
274}
275
276impl<'a, BE: Backend + 'a> GGSWToBackendMut<BE> for GGSWBackendMut<'a, BE> {
277    fn to_backend_mut(&mut self) -> GGSWBackendMut<'_, BE> {
278        GGSWBackendMut::from_inner(GGSW {
279            dsize: self.inner.dsize,
280            base2k: self.inner.base2k,
281            k_aux: self.inner.k_aux,
282            data: poulpy_hal::layouts::mat_znx_backend_mut_from_mut::<BE>(&mut self.inner.data),
283        })
284    }
285}
286
287impl<'a, BE: Backend + 'a> GGSWAtViewRef<BE> for GGSWBackendRef<'a, BE> {
288    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
289        GGSWBackendRef::at_view(self, row, col)
290    }
291}
292
293impl<'a, BE: Backend + 'a> GGSWAtViewRef<BE> for GGSWBackendMut<'a, BE> {
294    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
295        GGSWBackendMut::at_view(self, row, col)
296    }
297}
298
299impl<'a, BE: Backend + 'a> GGSWAtViewRef<BE> for &GGSWBackendRef<'a, BE> {
300    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
301        GGSWBackendRef::at_view(self, row, col)
302    }
303}
304
305impl<D: Data, W: ZnxWord> LWEInfos for GGSW<D, W> {
306    fn n(&self) -> Degree {
307        Degree(self.data.n() as u32)
308    }
309
310    fn base2k(&self) -> Base2K {
311        self.base2k
312    }
313
314    fn max_size(&self) -> usize {
315        self.data.size()
316    }
317
318    fn k(&self) -> TorusPrecision {
319        crate::layouts::key_k(self.base2k, self.dnum(), self.dsize, self.k_aux)
320    }
321}
322
323impl<D: Data, W: ZnxWord> GLWEInfos for GGSW<D, W> {
324    fn rank(&self) -> Rank {
325        Rank(self.data.cols_out() as u32 - 1)
326    }
327}
328
329impl<D: Data, W: ZnxWord> GGSWInfos for GGSW<D, W> {
330    fn k_aux(&self) -> TorusPrecision {
331        self.k_aux
332    }
333
334    fn dsize(&self) -> Dsize {
335        self.dsize
336    }
337
338    fn dnum(&self) -> Dnum {
339        Dnum(self.data.rows() as u32)
340    }
341}
342
343impl<D: HostDataRef, W: ZnxWord> fmt::Debug for GGSW<D, W> {
344    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345        write!(f, "{}", self.data)
346    }
347}
348
349impl<D: HostDataRef, W: ZnxWord> fmt::Display for GGSW<D, W> {
350    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351        write!(
352            f,
353            "(GGSW: k: {} base2k: {} dsize: {}) {}",
354            self.k().0,
355            self.base2k().0,
356            self.dsize().0,
357            self.data
358        )
359    }
360}
361
362impl<D: HostDataMut, W: ZnxWord> FillUniform for GGSW<D, W> {
363    fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
364        self.data.fill_uniform(log_bound, source);
365    }
366}
367
368impl<D: HostDataRef, W: ZnxWord> GGSW<D, W> {
369    pub fn at(&self, row: usize, col: usize) -> GLWE<&[u8], W> {
370        let data = self.data.at(row, col);
371        GLWE {
372            base2k: self.base2k,
373            k: self.k(),
374            data,
375        }
376    }
377}
378
379/// Backend-native shared view of one GLWE row.
380pub trait GGSWAtBackendRef<BE: Backend> {
381    fn at_backend(&self, row: usize, col: usize) -> GLWE<BE::BufRef<'_>, BE::ZnxWord>;
382}
383
384impl<BE: Backend> GGSWAtBackendRef<BE> for GGSW<BE::OwnedBuf, BE::ZnxWord> {
385    fn at_backend(&self, row: usize, col: usize) -> GLWE<BE::BufRef<'_>, BE::ZnxWord> {
386        let data = <MatZnx<BE::OwnedBuf, BE::ZnxWord> as MatZnxAtBackendRef<BE>>::at_backend(&self.data, row, col);
387        GLWE {
388            base2k: self.base2k,
389            k: self.k(),
390            data,
391        }
392    }
393}
394
395pub(crate) fn ggsw_at_backend_ref_from_ref<'a, 'b, BE: Backend>(
396    ggsw: &'a GGSW<BE::BufRef<'b>, BE::ZnxWord>,
397    row: usize,
398    col: usize,
399) -> GLWE<BE::BufRef<'a>, BE::ZnxWord> {
400    let data = poulpy_hal::layouts::mat_znx_at_backend_ref_from_ref::<BE>(&ggsw.data, row, col);
401    GLWE {
402        base2k: ggsw.base2k,
403        k: ggsw.k(),
404        data,
405    }
406}
407
408pub trait GGSWAtViewRef<BE: Backend> {
409    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE>;
410}
411
412impl<BE: Backend> GGSWAtViewRef<BE> for GGSW<BE::OwnedBuf, BE::ZnxWord> {
413    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
414        GLWEViewRef::from_inner(<GGSW<BE::OwnedBuf, BE::ZnxWord> as GGSWAtBackendRef<BE>>::at_backend(
415            self, row, col,
416        ))
417    }
418}
419
420pub(crate) fn ggsw_at_backend_ref_from_mut<'a, 'b, BE: Backend>(
421    ggsw: &'a GGSW<BE::BufMut<'b>, BE::ZnxWord>,
422    row: usize,
423    col: usize,
424) -> GLWE<BE::BufRef<'a>, BE::ZnxWord> {
425    let data = poulpy_hal::layouts::mat_znx_at_backend_ref_from_mut::<BE>(&ggsw.data, row, col);
426    GLWE {
427        base2k: ggsw.base2k,
428        k: ggsw.k(),
429        data,
430    }
431}
432
433impl<D: HostDataMut, W: ZnxWord> GGSW<D, W> {
434    pub fn at_mut(&mut self, row: usize, col: usize) -> GLWE<&mut [u8], W> {
435        let base2k = self.base2k;
436        let k = self.k();
437        let data = self.data.at_mut(row, col);
438        GLWE { base2k, k, data }
439    }
440}
441
442/// Backend-native mutable view of one GLWE row.
443pub trait GGSWAtBackendMut<BE: Backend> {
444    fn at_backend_mut(&mut self, row: usize, col: usize) -> GLWE<BE::BufMut<'_>, BE::ZnxWord>;
445}
446
447impl<BE: Backend> GGSWAtBackendMut<BE> for GGSW<BE::OwnedBuf, BE::ZnxWord> {
448    fn at_backend_mut(&mut self, row: usize, col: usize) -> GLWE<BE::BufMut<'_>, BE::ZnxWord> {
449        let base2k = self.base2k;
450        let k = self.k();
451        let data = <MatZnx<BE::OwnedBuf, BE::ZnxWord> as MatZnxAtBackendMut<BE>>::at_backend_mut(&mut self.data, row, col);
452        GLWE { base2k, k, data }
453    }
454}
455
456pub(crate) fn ggsw_at_backend_mut_from_mut<'a, 'b, BE: Backend>(
457    ggsw: &'a mut GGSW<BE::BufMut<'b>, BE::ZnxWord>,
458    row: usize,
459    col: usize,
460) -> GLWE<BE::BufMut<'a>, BE::ZnxWord> {
461    let base2k = ggsw.base2k;
462    let k = ggsw.k();
463    let data = poulpy_hal::layouts::mat_znx_at_backend_mut_from_mut::<BE>(&mut ggsw.data, row, col);
464    GLWE { base2k, k, data }
465}
466
467pub trait GGSWAtViewMut<BE: Backend> {
468    fn at_view_mut(&mut self, row: usize, col: usize) -> GLWEViewMut<'_, BE>;
469}
470
471impl<BE: Backend> GGSWAtViewMut<BE> for GGSW<BE::OwnedBuf, BE::ZnxWord> {
472    fn at_view_mut(&mut self, row: usize, col: usize) -> GLWEViewMut<'_, BE> {
473        GLWEViewMut::from_inner(<GGSW<BE::OwnedBuf, BE::ZnxWord> as GGSWAtBackendMut<BE>>::at_backend_mut(
474            self, row, col,
475        ))
476    }
477}
478
479impl<'a, BE: Backend + 'a> GGSWAtViewMut<BE> for GGSWBackendMut<'a, BE> {
480    fn at_view_mut(&mut self, row: usize, col: usize) -> GLWEViewMut<'_, BE> {
481        GGSWBackendMut::at_view_mut(self, row, col)
482    }
483}
484
485impl<D: Data, W: ZnxWord> GGSW<D, W> {
486    /// Zero-cost rename when both backends share the same `OwnedBuf`.
487    pub fn reinterpret<To>(self) -> GGSW<To::OwnedBuf, To::ZnxWord>
488    where
489        To: Backend<OwnedBuf = D, ZnxWord = W>,
490    {
491        let (n, rows, cols_in, cols_out, size) = (
492            self.data.n(),
493            self.data.rows(),
494            self.data.cols_in(),
495            self.data.cols_out(),
496            self.data.size(),
497        );
498        GGSW {
499            data: MatZnx::from_data(self.data.into_data(), n, rows, cols_in, cols_out, size),
500            k_aux: self.k_aux,
501            base2k: self.base2k,
502            dsize: self.dsize,
503        }
504    }
505}
506
507#[expect(
508    dead_code,
509    reason = "host-owned constructors are kept for serialization and host-only staging"
510)]
511impl<W: ZnxWord> GGSW<Vec<u8>, W> {
512    pub(crate) fn alloc_from_infos<A>(infos: &A) -> Self
513    where
514        A: GGSWInfos,
515    {
516        Self::alloc(
517            infos.n(),
518            infos.base2k(),
519            infos.dnum(),
520            infos.dsize(),
521            infos.k_aux(),
522            infos.rank(),
523        )
524    }
525
526    pub(crate) fn alloc(n: Degree, base2k: Base2K, dnum: Dnum, dsize: Dsize, k_aux: TorusPrecision, rank: Rank) -> Self {
527        let size: usize = crate::layouts::key_size(base2k, dnum, dsize, k_aux);
528
529        GGSW {
530            data: MatZnx::from_data(
531                poulpy_hal::layouts::HostBytesBackend::alloc_bytes(MatZnx::<Vec<u8>, W>::bytes_of(
532                    n.into(),
533                    dnum.into(),
534                    (rank + 1).into(),
535                    (rank + 1).into(),
536                    size,
537                )),
538                n.into(),
539                dnum.into(),
540                (rank + 1).into(),
541                (rank + 1).into(),
542                size,
543            ),
544            k_aux,
545            base2k,
546            dsize,
547        }
548    }
549
550    pub fn bytes_of_from_infos<A>(infos: &A) -> usize
551    where
552        A: GGSWInfos,
553    {
554        Self::bytes_of(
555            infos.n(),
556            infos.base2k(),
557            infos.dnum(),
558            infos.dsize(),
559            infos.k_aux(),
560            infos.rank(),
561        )
562    }
563
564    pub fn bytes_of(n: Degree, base2k: Base2K, dnum: Dnum, dsize: Dsize, k_aux: TorusPrecision, rank: Rank) -> usize {
565        let size: usize = crate::layouts::key_size(base2k, dnum, dsize, k_aux);
566
567        MatZnx::<Vec<u8>, W>::bytes_of(n.into(), dnum.into(), (rank + 1).into(), (rank + 1).into(), size)
568    }
569}
570
571use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
572
573impl<D: HostDataMut, W: ZnxWord> ReaderFrom for GGSW<D, W> {
574    fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
575        self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
576        self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
577        self.k_aux = TorusPrecision(reader.read_u32::<LittleEndian>()?);
578        self.data.read_from(reader)
579    }
580}
581
582impl<D: HostDataRef, W: ZnxWord> WriterTo for GGSW<D, W> {
583    fn write_to<Wr: std::io::Write>(&self, writer: &mut Wr) -> std::io::Result<()> {
584        writer.write_u32::<LittleEndian>(self.base2k.into())?;
585        writer.write_u32::<LittleEndian>(self.dsize.into())?;
586        writer.write_u32::<LittleEndian>(self.k_aux.into())?;
587        self.data.write_to(writer)
588    }
589}
590
591pub trait GGSWToBackendMut<BE: Backend>: GGSWToBackendRef<BE> {
592    fn to_backend_mut(&mut self) -> GGSWBackendMut<'_, BE>;
593}
594
595impl<BE: Backend, D: Data> GGSWToBackendMut<BE> for GGSW<D, BE::ZnxWord>
596where
597    MatZnx<D, BE::ZnxWord>: MatZnxToBackendRef<BE> + MatZnxToBackendMut<BE>,
598{
599    fn to_backend_mut(&mut self) -> GGSWBackendMut<'_, BE> {
600        GGSWBackendMut::from_inner(GGSW {
601            dsize: self.dsize,
602            base2k: self.base2k,
603            k_aux: self.k_aux,
604            data: self.data.to_backend_mut(),
605        })
606    }
607}
608
609impl<'b, BE: Backend + 'b> GGSWToBackendRef<BE> for &mut GGSW<BE::BufMut<'b>, BE::ZnxWord> {
610    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE> {
611        GGSWBackendRef::from_inner(GGSW {
612            dsize: self.dsize,
613            base2k: self.base2k,
614            k_aux: self.k_aux,
615            data: poulpy_hal::layouts::mat_znx_backend_ref_from_mut::<BE>(&self.data),
616        })
617    }
618}
619
620impl<'b, BE: Backend + 'b> GGSWToBackendMut<BE> for &mut GGSW<BE::BufMut<'b>, BE::ZnxWord> {
621    fn to_backend_mut(&mut self) -> GGSWBackendMut<'_, BE> {
622        ggsw_backend_mut_from_mut::<BE>(self)
623    }
624}
625
626pub fn ggsw_backend_mut_from_mut<'a, 'b, BE: Backend>(ggsw: &'a mut GGSW<BE::BufMut<'b>, BE::ZnxWord>) -> GGSWBackendMut<'a, BE> {
627    GGSWBackendMut::from_inner(GGSW {
628        dsize: ggsw.dsize,
629        base2k: ggsw.base2k,
630        k_aux: ggsw.k_aux,
631        data: poulpy_hal::layouts::mat_znx_backend_mut_from_mut::<BE>(&mut ggsw.data),
632    })
633}
634
635/// Row-view adapter that lets a `GGSWToBackendMut` type satisfy both `GGSWAtViewRef` and
636/// `GGSWAtViewMut` simultaneously, which is required by several default algorithms that need
637/// to read and write individual GLWE rows through the trait interface.
638pub struct GGSWBackendRowViewMut<'a, BE: Backend + 'a> {
639    inner: GGSWBackendMut<'a, BE>,
640}
641
642impl<'a, BE: Backend + 'a> GGSWBackendRowViewMut<'a, BE> {
643    pub fn from_inner(inner: GGSWBackendMut<'a, BE>) -> Self {
644        Self { inner }
645    }
646}
647
648impl<'a, BE: Backend + 'a> LWEInfos for GGSWBackendRowViewMut<'a, BE> {
649    fn base2k(&self) -> Base2K {
650        self.inner.base2k()
651    }
652    fn n(&self) -> Degree {
653        self.inner.n()
654    }
655    fn max_size(&self) -> usize {
656        self.inner.max_size()
657    }
658    fn k(&self) -> TorusPrecision {
659        self.inner.k()
660    }
661}
662
663impl<'a, BE: Backend + 'a> GLWEInfos for GGSWBackendRowViewMut<'a, BE> {
664    fn rank(&self) -> Rank {
665        self.inner.rank()
666    }
667}
668
669impl<'a, BE: Backend + 'a> GGSWInfos for GGSWBackendRowViewMut<'a, BE> {
670    fn k_aux(&self) -> TorusPrecision {
671        self.inner.k_aux()
672    }
673    fn dnum(&self) -> Dnum {
674        self.inner.dnum()
675    }
676    fn dsize(&self) -> Dsize {
677        self.inner.dsize()
678    }
679}
680
681impl<'a, BE: Backend + 'a> GGSWToBackendRef<BE> for GGSWBackendRowViewMut<'a, BE> {
682    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE> {
683        self.inner.to_backend_ref()
684    }
685}
686
687impl<'a, BE: Backend + 'a> GGSWToBackendMut<BE> for GGSWBackendRowViewMut<'a, BE> {
688    fn to_backend_mut(&mut self) -> GGSWBackendMut<'_, BE> {
689        GGSWBackendMut::from_inner(GGSW {
690            dsize: self.inner.inner.dsize,
691            base2k: self.inner.inner.base2k,
692            k_aux: self.inner.inner.k_aux,
693            data: poulpy_hal::layouts::mat_znx_backend_mut_from_mut::<BE>(&mut self.inner.inner.data),
694        })
695    }
696}
697
698impl<'a, BE: Backend + 'a> GGSWAtViewRef<BE> for GGSWBackendRowViewMut<'a, BE> {
699    fn at_view(&self, row: usize, col: usize) -> GLWEViewRef<'_, BE> {
700        self.inner.at_view(row, col)
701    }
702}
703
704impl<'a, BE: Backend + 'a> GGSWAtViewMut<BE> for GGSWBackendRowViewMut<'a, BE> {
705    fn at_view_mut(&mut self, row: usize, col: usize) -> GLWEViewMut<'_, BE> {
706        self.inner.at_view_mut(row, col)
707    }
708}
709
710pub trait GGSWToBackendRef<BE: Backend> {
711    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE>;
712}
713
714impl<BE: Backend, D: Data> GGSWToBackendRef<BE> for GGSW<D, BE::ZnxWord>
715where
716    MatZnx<D, BE::ZnxWord>: MatZnxToBackendRef<BE>,
717{
718    fn to_backend_ref(&self) -> GGSWBackendRef<'_, BE> {
719        GGSWBackendRef::from_inner(GGSW {
720            dsize: self.dsize,
721            base2k: self.base2k,
722            k_aux: self.k_aux,
723            data: self.data.to_backend_ref(),
724        })
725    }
726}