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