Skip to main content

poulpy_core/layouts/
mod.rs

1//! Ciphertext, key, plaintext, and secret-key layout types.
2//!
3//! This module defines the in-memory representations for every
4//! cryptographic object manipulated by `poulpy-core`. Types are
5//! organised into three sub-layers:
6//!
7//! * **Standard** (this module) -- serialisable, platform-independent
8//!   byte layouts backed by [`poulpy_hal::layouts::VecZnx`] /
9//!   [`poulpy_hal::layouts::MatZnx`].
10//! * **[`compressed`]** -- seed-compressed variants that store only
11//!   the body; the mask is regenerated from a 32-byte PRNG seed.
12//! * **[`prepared`]** -- DFT-domain representations tied to a
13//!   specific [`poulpy_hal::layouts::Backend`], used for fast
14//!   polynomial multiplication.
15//!
16//! # Parameter newtypes
17//!
18//! Domain-specific quantities are wrapped in [`u32`]-backed newtypes
19//! generated by the `newtype_u32!` macro:
20//!
21//! | Type | Meaning |
22//! |---|---|
23//! | [`Degree`] | Ring polynomial degree *N* (always a power of two) |
24//! | [`Base2K`] | Base-2 logarithm of the limb radix used in the CRT/digit decomposition |
25//! | [`TorusPrecision`] | Total number of precision bits over the torus *T = R/Z* |
26//! | [`Rank`] | GLWE rank (number of mask polynomials; 0 for plain LWE) |
27//! | [`Dnum`] | Number of gadget-decomposition digits |
28//! | [`Dsize`] | Size (in limbs) of each gadget digit |
29
30#[macro_use]
31mod macros;
32
33mod gglwe;
34mod gglwe_to_ggsw_key;
35mod ggsw;
36mod glwe;
37mod glwe_automorphism_key;
38mod glwe_plaintext;
39mod glwe_public_key;
40mod glwe_secret;
41mod glwe_secret_tensor;
42mod glwe_switching_key;
43mod glwe_tensor;
44mod glwe_tensor_key;
45mod glwe_to_lwe_key;
46mod lwe;
47mod lwe_plaintext;
48mod lwe_secret;
49mod lwe_switching_key;
50mod lwe_to_glwe_key;
51mod scratch_views;
52
53pub mod compressed;
54pub mod prepared;
55
56pub use self::compressed::{
57    GGLWECompressed, GGLWECompressedSeed, GGLWECompressedSeedMut, GGLWECompressedToBackendMut, GGLWECompressedToBackendRef,
58    GGLWEDecompress, GGLWEToGGSWKeyCompressed, GGLWEToGGSWKeyCompressedToBackendMut, GGLWEToGGSWKeyCompressedToBackendRef,
59    GGLWEToGGSWKeyDecompress, GGSWCompressed, GGSWCompressedSeed, GGSWCompressedSeedMut, GGSWCompressedToBackendMut,
60    GGSWCompressedToBackendRef, GGSWDecompress, GLWEAutomorphismKeyCompressed, GLWEAutomorphismKeyDecompress, GLWECompressed,
61    GLWECompressedSeed, GLWECompressedSeedMut, GLWECompressedToBackendMut, GLWECompressedToBackendRef, GLWEDecompress,
62    GLWESwitchingKeyCompressed, GLWESwitchingKeyDecompress, GLWETensorKeyCompressed, GLWETensorKeyDecompress,
63    GLWEToLWESwitchingKeyCompressed, GLWEToLWESwitchingKeyDecompress, LWECompressed, LWECompressedToBackendMut,
64    LWECompressedToBackendRef, LWEDecompress, LWESwitchingKeyCompressed, LWESwitchingKeyDecompress, LWEToGLWEKeyCompressed,
65    LWEToGLWEKeyDecompress,
66};
67pub use gglwe::*;
68pub use gglwe_to_ggsw_key::*;
69pub use ggsw::*;
70pub use glwe::*;
71pub use glwe_automorphism_key::*;
72pub use glwe_plaintext::*;
73pub use glwe_public_key::*;
74pub use glwe_secret::*;
75pub use glwe_secret_tensor::*;
76pub use glwe_switching_key::*;
77pub use glwe_tensor::*;
78pub use glwe_tensor_key::*;
79pub use glwe_to_lwe_key::*;
80pub use lwe::*;
81pub use lwe_plaintext::*;
82pub use lwe_secret::*;
83pub use lwe_switching_key::*;
84pub use lwe_to_glwe_key::*;
85pub use prepared::*;
86pub use scratch_views::*;
87
88use crate::dist::Distribution;
89use poulpy_hal::layouts::{Backend, Data, MatZnx, Module, ScalarZnx, VecZnx};
90
91/// Backend-indexed ownership aliases for the non-prepared layouts.
92///
93/// These resolve to `<Layout><<BE as Backend>::OwnedBuf>` so that user
94/// code can declare types in terms of the owning backend instead of the
95/// raw storage type. On CPU backends this is just `Vec<u8>`; on future
96/// device backends it is the backend's device buffer type.
97pub type BackendGLWE<BE> = GLWE<<BE as Backend>::OwnedBuf>;
98pub type BackendGGLWE<BE> = GGLWE<<BE as Backend>::OwnedBuf>;
99pub type BackendGGSW<BE> = GGSW<<BE as Backend>::OwnedBuf>;
100pub type BackendLWE<BE> = LWE<<BE as Backend>::OwnedBuf>;
101pub type BackendGLWESecret<BE> = GLWESecret<<BE as Backend>::OwnedBuf>;
102pub type BackendLWESecret<BE> = LWESecret<<BE as Backend>::OwnedBuf>;
103pub type BackendGLWEPlaintext<BE> = GLWEPlaintext<<BE as Backend>::OwnedBuf>;
104pub type BackendLWEPlaintext<BE> = LWEPlaintext<<BE as Backend>::OwnedBuf>;
105pub type BackendGLWEPrepared<BE> = GLWEPrepared<<BE as Backend>::OwnedBuf, BE>;
106pub type BackendGGLWEPrepared<BE> = GGLWEPrepared<<BE as Backend>::OwnedBuf, BE>;
107pub type BackendGGSWPrepared<BE> = GGSWPrepared<<BE as Backend>::OwnedBuf, BE>;
108pub type BackendGLWESecretPrepared<BE> = GLWESecretPrepared<<BE as Backend>::OwnedBuf, BE>;
109pub type BackendGLWEPublicKeyPrepared<BE> = GLWEPublicKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
110pub type BackendGLWESecretTensorPrepared<BE> = GLWESecretTensorPrepared<<BE as Backend>::OwnedBuf, BE>;
111pub type BackendGLWESwitchingKeyPrepared<BE> = GLWESwitchingKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
112pub type BackendGLWEAutomorphismKeyPrepared<BE> = GLWEAutomorphismKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
113pub type BackendGLWETensorKeyPrepared<BE> = GLWETensorKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
114pub type BackendGLWEToLWEKeyPrepared<BE> = GLWEToLWEKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
115pub type BackendLWESwitchingKeyPrepared<BE> = LWESwitchingKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
116pub type BackendLWEToGLWEKeyPrepared<BE> = LWEToGLWEKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
117pub type BackendGGLWEToGGSWKeyPrepared<BE> = GGLWEToGGSWKeyPrepared<<BE as Backend>::OwnedBuf, BE>;
118
119/// Provides access to the ring polynomial degree *N*.
120pub trait GetDegree {
121    /// Returns the ring degree *N* as a [`Degree`].
122    fn ring_degree(&self) -> Degree;
123}
124
125impl<B: Backend> GetDegree for Module<B> {
126    fn ring_degree(&self) -> Degree {
127        Self::n(self).into()
128    }
129}
130
131/// Backend-native wrapper allocation helpers hung off a [`Module`].
132///
133/// This mirrors the `poulpy-hal` allocation model: callers allocate through a
134/// module/context object instead of using static layout constructors directly.
135pub trait ModuleCoreAlloc {
136    type OwnedBuf: Data;
137
138    fn glwe_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWE<Self::OwnedBuf>;
139    fn glwe_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWE<Self::OwnedBuf>;
140
141    fn gglwe_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWE<Self::OwnedBuf>;
142    fn gglwe_alloc(
143        &self,
144        base2k: Base2K,
145        k: TorusPrecision,
146        rank_in: Rank,
147        rank_out: Rank,
148        dnum: Dnum,
149        dsize: Dsize,
150    ) -> GGLWE<Self::OwnedBuf>;
151
152    fn ggsw_alloc_from_infos<A: GGSWInfos>(&self, infos: &A) -> GGSW<Self::OwnedBuf>;
153    fn ggsw_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> GGSW<Self::OwnedBuf>;
154
155    fn glwe_plaintext_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWEPlaintext<Self::OwnedBuf>;
156    fn glwe_plaintext_alloc(&self, base2k: Base2K, k: TorusPrecision) -> GLWEPlaintext<Self::OwnedBuf>;
157
158    fn glwe_secret_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWESecret<Self::OwnedBuf>;
159    fn glwe_secret_alloc(&self, rank: Rank) -> GLWESecret<Self::OwnedBuf>;
160
161    fn glwe_secret_tensor_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWESecretTensor<Self::OwnedBuf>;
162    fn glwe_secret_tensor_alloc(&self, rank: Rank) -> GLWESecretTensor<Self::OwnedBuf>;
163
164    fn glwe_tensor_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWETensor<Self::OwnedBuf>;
165    fn glwe_tensor_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWETensor<Self::OwnedBuf>;
166
167    fn glwe_public_key_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWEPublicKey<Self::OwnedBuf>;
168    fn glwe_public_key_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWEPublicKey<Self::OwnedBuf>;
169
170    fn glwe_switching_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWESwitchingKey<Self::OwnedBuf>;
171    fn glwe_switching_key_alloc(
172        &self,
173        base2k: Base2K,
174        k: TorusPrecision,
175        rank_in: Rank,
176        rank_out: Rank,
177        dnum: Dnum,
178        dsize: Dsize,
179    ) -> GLWESwitchingKey<Self::OwnedBuf>;
180
181    fn glwe_automorphism_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEAutomorphismKey<Self::OwnedBuf>;
182    fn glwe_automorphism_key_alloc(
183        &self,
184        base2k: Base2K,
185        k: TorusPrecision,
186        rank: Rank,
187        dnum: Dnum,
188        dsize: Dsize,
189    ) -> GLWEAutomorphismKey<Self::OwnedBuf>;
190
191    fn glwe_tensor_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWETensorKey<Self::OwnedBuf>;
192    fn glwe_tensor_key_alloc(
193        &self,
194        base2k: Base2K,
195        k: TorusPrecision,
196        rank: Rank,
197        dnum: Dnum,
198        dsize: Dsize,
199    ) -> GLWETensorKey<Self::OwnedBuf>;
200
201    fn glwe_to_lwe_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEToLWEKey<Self::OwnedBuf>;
202    fn glwe_to_lwe_key_alloc(&self, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum)
203    -> GLWEToLWEKey<Self::OwnedBuf>;
204
205    fn gglwe_to_ggsw_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWEToGGSWKey<Self::OwnedBuf>;
206    fn gglwe_to_ggsw_key_alloc(
207        &self,
208        base2k: Base2K,
209        k: TorusPrecision,
210        rank: Rank,
211        dnum: Dnum,
212        dsize: Dsize,
213    ) -> GGLWEToGGSWKey<Self::OwnedBuf>;
214
215    fn lwe_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWE<Self::OwnedBuf>;
216    fn lwe_alloc(&self, n: Degree, base2k: Base2K, k: TorusPrecision) -> LWE<Self::OwnedBuf>;
217
218    fn lwe_plaintext_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWEPlaintext<Self::OwnedBuf>;
219    fn lwe_plaintext_alloc(&self, base2k: Base2K, k: TorusPrecision) -> LWEPlaintext<Self::OwnedBuf>;
220
221    fn lwe_secret_alloc(&self, n: Degree) -> LWESecret<Self::OwnedBuf>;
222
223    fn lwe_switching_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWESwitchingKey<Self::OwnedBuf>;
224    fn lwe_switching_key_alloc(
225        &self,
226        n: Degree,
227        base2k: Base2K,
228        k: TorusPrecision,
229        dnum: Dnum,
230    ) -> LWESwitchingKey<Self::OwnedBuf>;
231
232    fn lwe_to_glwe_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWEToGLWEKey<Self::OwnedBuf>;
233    fn lwe_to_glwe_key_alloc(
234        &self,
235        n: Degree,
236        base2k: Base2K,
237        k: TorusPrecision,
238        rank_out: Rank,
239        dnum: Dnum,
240    ) -> LWEToGLWEKey<Self::OwnedBuf>;
241}
242
243impl<B: Backend> ModuleCoreAlloc for Module<B> {
244    type OwnedBuf = B::OwnedBuf;
245
246    fn glwe_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWE<B::OwnedBuf> {
247        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
248        GLWE {
249            data: VecZnx::from_data(
250                B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(
251                    infos.n().as_usize(),
252                    (infos.rank() + 1).as_usize(),
253                    size,
254                )),
255                infos.n().as_usize(),
256                (infos.rank() + 1).as_usize(),
257                size,
258            ),
259            base2k: infos.base2k(),
260        }
261    }
262    fn glwe_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWE<B::OwnedBuf> {
263        self.glwe_alloc_from_infos(&GLWELayout {
264            n: self.ring_degree(),
265            base2k,
266            k,
267            rank,
268        })
269    }
270
271    fn gglwe_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWE<B::OwnedBuf> {
272        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
273        assert!(
274            size as u32 > infos.dsize().0,
275            "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
276            infos.dsize().0
277        );
278        assert!(
279            infos.dnum().0 * infos.dsize().0 <= size as u32,
280            "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
281            infos.dnum().0,
282            infos.dsize().0,
283        );
284
285        GGLWE {
286            data: MatZnx::from_data(
287                B::alloc_zeroed_bytes(MatZnx::<Vec<u8>>::bytes_of(
288                    infos.n().as_usize(),
289                    infos.dnum().as_usize(),
290                    infos.rank_in().as_usize(),
291                    (infos.rank_out() + 1).as_usize(),
292                    size,
293                )),
294                infos.n().as_usize(),
295                infos.dnum().as_usize(),
296                infos.rank_in().as_usize(),
297                (infos.rank_out() + 1).as_usize(),
298                size,
299            ),
300            base2k: infos.base2k(),
301            dsize: infos.dsize(),
302        }
303    }
304    fn gglwe_alloc(
305        &self,
306        base2k: Base2K,
307        k: TorusPrecision,
308        rank_in: Rank,
309        rank_out: Rank,
310        dnum: Dnum,
311        dsize: Dsize,
312    ) -> GGLWE<B::OwnedBuf> {
313        self.gglwe_alloc_from_infos(&GGLWELayout {
314            n: self.ring_degree(),
315            base2k,
316            k,
317            rank_in,
318            rank_out,
319            dnum,
320            dsize,
321        })
322    }
323
324    fn ggsw_alloc_from_infos<A: GGSWInfos>(&self, infos: &A) -> GGSW<B::OwnedBuf> {
325        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
326        assert!(
327            size as u32 > infos.dsize().0,
328            "invalid ggsw: ceil(k/base2k): {size} <= dsize: {}",
329            infos.dsize().0
330        );
331        assert!(
332            infos.dnum().0 * infos.dsize().0 <= size as u32,
333            "invalid ggsw: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
334            infos.dnum().0,
335            infos.dsize().0,
336        );
337
338        GGSW {
339            data: MatZnx::from_data(
340                B::alloc_zeroed_bytes(MatZnx::<Vec<u8>>::bytes_of(
341                    infos.n().as_usize(),
342                    infos.dnum().as_usize(),
343                    (infos.rank() + 1).as_usize(),
344                    (infos.rank() + 1).as_usize(),
345                    size,
346                )),
347                infos.n().as_usize(),
348                infos.dnum().as_usize(),
349                (infos.rank() + 1).as_usize(),
350                (infos.rank() + 1).as_usize(),
351                size,
352            ),
353            base2k: infos.base2k(),
354            dsize: infos.dsize(),
355        }
356    }
357    fn ggsw_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> GGSW<B::OwnedBuf> {
358        self.ggsw_alloc_from_infos(&GGSWLayout {
359            n: self.ring_degree(),
360            base2k,
361            k,
362            rank,
363            dnum,
364            dsize,
365        })
366    }
367
368    fn glwe_plaintext_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWEPlaintext<B::OwnedBuf> {
369        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
370        GLWEPlaintext {
371            data: VecZnx::from_data(
372                B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(infos.n().as_usize(), 1, size)),
373                infos.n().as_usize(),
374                1,
375                size,
376            ),
377            base2k: infos.base2k(),
378        }
379    }
380    fn glwe_plaintext_alloc(&self, base2k: Base2K, k: TorusPrecision) -> GLWEPlaintext<B::OwnedBuf> {
381        self.glwe_plaintext_alloc_from_infos(&GLWEPlaintextLayout {
382            n: self.ring_degree(),
383            base2k,
384            k,
385        })
386    }
387
388    fn glwe_secret_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWESecret<B::OwnedBuf> {
389        GLWESecret {
390            data: ScalarZnx::from_data(
391                B::alloc_zeroed_bytes(ScalarZnx::<Vec<u8>>::bytes_of(infos.n().as_usize(), infos.rank().as_usize())),
392                infos.n().as_usize(),
393                infos.rank().as_usize(),
394            ),
395            dist: Distribution::NONE,
396        }
397    }
398    fn glwe_secret_alloc(&self, rank: Rank) -> GLWESecret<B::OwnedBuf> {
399        self.glwe_secret_alloc_from_infos(&GLWESecretLayout {
400            n: self.ring_degree(),
401            rank,
402        })
403    }
404
405    fn glwe_secret_tensor_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWESecretTensor<B::OwnedBuf> {
406        let pairs = GLWESecretTensor::<Vec<u8>>::pairs(infos.rank().as_usize());
407        GLWESecretTensor {
408            data: ScalarZnx::from_data(
409                B::alloc_zeroed_bytes(ScalarZnx::<Vec<u8>>::bytes_of(infos.n().as_usize(), pairs)),
410                infos.n().as_usize(),
411                pairs,
412            ),
413            rank: infos.rank(),
414            dist: Distribution::NONE,
415        }
416    }
417    fn glwe_secret_tensor_alloc(&self, rank: Rank) -> GLWESecretTensor<B::OwnedBuf> {
418        self.glwe_secret_tensor_alloc_from_infos(&GLWESecretLayout {
419            n: self.ring_degree(),
420            rank,
421        })
422    }
423
424    fn glwe_tensor_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWETensor<B::OwnedBuf> {
425        let cols = infos.rank().as_usize() + 1;
426        let pairs = (((cols + 1) * cols) >> 1).max(1);
427        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
428        GLWETensor {
429            data: VecZnx::from_data(
430                B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(infos.n().as_usize(), pairs, size)),
431                infos.n().as_usize(),
432                pairs,
433                size,
434            ),
435            base2k: infos.base2k(),
436            rank: infos.rank(),
437        }
438    }
439    fn glwe_tensor_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWETensor<B::OwnedBuf> {
440        self.glwe_tensor_alloc_from_infos(&GLWELayout {
441            n: self.ring_degree(),
442            base2k,
443            k,
444            rank,
445        })
446    }
447
448    fn glwe_public_key_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWEPublicKey<B::OwnedBuf> {
449        GLWEPublicKey {
450            key: self.glwe_alloc_from_infos(infos),
451            dist: Distribution::NONE,
452        }
453    }
454    fn glwe_public_key_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWEPublicKey<B::OwnedBuf> {
455        self.glwe_public_key_alloc_from_infos(&GLWELayout {
456            n: self.ring_degree(),
457            base2k,
458            k,
459            rank,
460        })
461    }
462
463    fn glwe_switching_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWESwitchingKey<B::OwnedBuf> {
464        GLWESwitchingKey {
465            key: self.gglwe_alloc_from_infos(infos),
466            input_degree: Degree(0),
467            output_degree: Degree(0),
468        }
469    }
470    fn glwe_switching_key_alloc(
471        &self,
472        base2k: Base2K,
473        k: TorusPrecision,
474        rank_in: Rank,
475        rank_out: Rank,
476        dnum: Dnum,
477        dsize: Dsize,
478    ) -> GLWESwitchingKey<B::OwnedBuf> {
479        self.glwe_switching_key_alloc_from_infos(&GGLWELayout {
480            n: self.ring_degree(),
481            base2k,
482            k,
483            rank_in,
484            rank_out,
485            dnum,
486            dsize,
487        })
488    }
489
490    fn glwe_automorphism_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEAutomorphismKey<B::OwnedBuf> {
491        GLWEAutomorphismKey {
492            key: self.gglwe_alloc_from_infos(infos),
493            p: 0,
494        }
495    }
496    fn glwe_automorphism_key_alloc(
497        &self,
498        base2k: Base2K,
499        k: TorusPrecision,
500        rank: Rank,
501        dnum: Dnum,
502        dsize: Dsize,
503    ) -> GLWEAutomorphismKey<B::OwnedBuf> {
504        self.glwe_automorphism_key_alloc_from_infos(&GGLWELayout {
505            n: self.ring_degree(),
506            base2k,
507            k,
508            rank_in: rank,
509            rank_out: rank,
510            dnum,
511            dsize,
512        })
513    }
514
515    fn glwe_tensor_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWETensorKey<B::OwnedBuf> {
516        GLWETensorKey(self.gglwe_alloc_from_infos(infos))
517    }
518    fn glwe_tensor_key_alloc(
519        &self,
520        base2k: Base2K,
521        k: TorusPrecision,
522        rank: Rank,
523        dnum: Dnum,
524        dsize: Dsize,
525    ) -> GLWETensorKey<B::OwnedBuf> {
526        let pairs = (((rank.0 + 1) * rank.0) >> 1).max(1);
527        self.glwe_tensor_key_alloc_from_infos(&GGLWELayout {
528            n: self.ring_degree(),
529            base2k,
530            k,
531            rank_in: Rank(pairs),
532            rank_out: rank,
533            dnum,
534            dsize,
535        })
536    }
537
538    fn glwe_to_lwe_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEToLWEKey<B::OwnedBuf> {
539        assert_eq!(infos.rank_out().0, 1, "rank_out > 1 is not supported for GLWEToLWEKey");
540        assert_eq!(infos.dsize().0, 1, "dsize > 1 is not supported for GLWEToLWEKey");
541        GLWEToLWEKey(self.glwe_switching_key_alloc_from_infos(infos))
542    }
543    fn glwe_to_lwe_key_alloc(&self, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum) -> GLWEToLWEKey<B::OwnedBuf> {
544        self.glwe_to_lwe_key_alloc_from_infos(&GGLWELayout {
545            n: self.ring_degree(),
546            base2k,
547            k,
548            rank_in,
549            rank_out: Rank(1),
550            dnum,
551            dsize: Dsize(1),
552        })
553    }
554
555    fn gglwe_to_ggsw_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWEToGGSWKey<B::OwnedBuf> {
556        assert_eq!(
557            infos.rank_in(),
558            infos.rank_out(),
559            "rank_in != rank_out is not supported for GGLWEToGGSWKey"
560        );
561        GGLWEToGGSWKey {
562            keys: (0..infos.rank().as_usize())
563                .map(|_| self.gglwe_alloc_from_infos(infos))
564                .collect(),
565        }
566    }
567    fn gglwe_to_ggsw_key_alloc(
568        &self,
569        base2k: Base2K,
570        k: TorusPrecision,
571        rank: Rank,
572        dnum: Dnum,
573        dsize: Dsize,
574    ) -> GGLWEToGGSWKey<B::OwnedBuf> {
575        self.gglwe_to_ggsw_key_alloc_from_infos(&GGLWELayout {
576            n: self.ring_degree(),
577            base2k,
578            k,
579            rank_in: rank,
580            rank_out: rank,
581            dnum,
582            dsize,
583        })
584    }
585
586    fn lwe_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWE<B::OwnedBuf> {
587        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
588        let n = infos.n().as_usize();
589        LWE {
590            body: VecZnx::from_data(B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(1, 1, size)), 1, 1, size),
591            mask: VecZnx::from_data(B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(n, 1, size)), n, 1, size),
592            base2k: infos.base2k(),
593        }
594    }
595    fn lwe_alloc(&self, n: Degree, base2k: Base2K, k: TorusPrecision) -> LWE<B::OwnedBuf> {
596        self.lwe_alloc_from_infos(&LWELayout { n, base2k, k })
597    }
598
599    fn lwe_plaintext_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWEPlaintext<B::OwnedBuf> {
600        let size = infos.max_k().as_usize().div_ceil(infos.base2k().as_usize());
601        LWEPlaintext {
602            data: VecZnx::from_data(B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(1, 1, size)), 1, 1, size),
603            base2k: infos.base2k(),
604        }
605    }
606    fn lwe_plaintext_alloc(&self, base2k: Base2K, k: TorusPrecision) -> LWEPlaintext<B::OwnedBuf> {
607        let size = k.as_usize().div_ceil(base2k.as_usize());
608        LWEPlaintext {
609            data: VecZnx::from_data(B::alloc_zeroed_bytes(VecZnx::<Vec<u8>>::bytes_of(1, 1, size)), 1, 1, size),
610            base2k,
611        }
612    }
613
614    fn lwe_secret_alloc(&self, n: Degree) -> LWESecret<B::OwnedBuf> {
615        LWESecret {
616            data: ScalarZnx::from_data(
617                B::alloc_zeroed_bytes(ScalarZnx::<Vec<u8>>::bytes_of(n.as_usize(), 1)),
618                n.as_usize(),
619                1,
620            ),
621            dist: Distribution::NONE,
622        }
623    }
624
625    fn lwe_switching_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWESwitchingKey<B::OwnedBuf> {
626        assert_eq!(infos.dsize().0, 1, "dsize > 1 is not supported for LWESwitchingKey");
627        assert_eq!(infos.rank_in().0, 1, "rank_in > 1 is not supported for LWESwitchingKey");
628        assert_eq!(infos.rank_out().0, 1, "rank_out > 1 is not supported for LWESwitchingKey");
629        LWESwitchingKey(self.glwe_switching_key_alloc_from_infos(infos))
630    }
631    fn lwe_switching_key_alloc(&self, n: Degree, base2k: Base2K, k: TorusPrecision, dnum: Dnum) -> LWESwitchingKey<B::OwnedBuf> {
632        self.lwe_switching_key_alloc_from_infos(&LWESwitchingKeyLayout { n, base2k, k, dnum })
633    }
634
635    fn lwe_to_glwe_key_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWEToGLWEKey<B::OwnedBuf> {
636        assert_eq!(infos.rank_in().0, 1, "rank_in > 1 is not supported for LWEToGLWEKey");
637        assert_eq!(infos.dsize().0, 1, "dsize > 1 is not supported for LWEToGLWEKey");
638        LWEToGLWEKey(self.glwe_switching_key_alloc_from_infos(infos))
639    }
640    fn lwe_to_glwe_key_alloc(
641        &self,
642        n: Degree,
643        base2k: Base2K,
644        k: TorusPrecision,
645        rank_out: Rank,
646        dnum: Dnum,
647    ) -> LWEToGLWEKey<B::OwnedBuf> {
648        self.lwe_to_glwe_key_alloc_from_infos(&GGLWELayout {
649            n,
650            base2k,
651            k,
652            rank_in: Rank(1),
653            rank_out,
654            dnum,
655            dsize: Dsize(1),
656        })
657    }
658}
659
660/// Host-owned compressed wrapper allocation helpers hung off a [`Module`].
661///
662/// This mirrors [`ModuleCoreAlloc`], but for seed-compressed ciphertext and
663/// key layouts.
664pub trait ModuleCoreCompressedAlloc {
665    fn glwe_compressed_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWECompressed<Vec<u8>>;
666    fn glwe_compressed_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWECompressed<Vec<u8>>;
667
668    fn lwe_compressed_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWECompressed<Vec<u8>>;
669    fn lwe_compressed_alloc(&self, base2k: Base2K, k: TorusPrecision) -> LWECompressed<Vec<u8>>;
670
671    fn gglwe_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWECompressed<Vec<u8>>;
672    fn gglwe_compressed_alloc(
673        &self,
674        base2k: Base2K,
675        k: TorusPrecision,
676        rank_in: Rank,
677        rank_out: Rank,
678        dnum: Dnum,
679        dsize: Dsize,
680    ) -> GGLWECompressed<Vec<u8>>;
681
682    fn ggsw_compressed_alloc_from_infos<A: GGSWInfos>(&self, infos: &A) -> GGSWCompressed<Vec<u8>>;
683    fn ggsw_compressed_alloc(
684        &self,
685        base2k: Base2K,
686        k: TorusPrecision,
687        rank: Rank,
688        dnum: Dnum,
689        dsize: Dsize,
690    ) -> GGSWCompressed<Vec<u8>>;
691
692    fn glwe_switching_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWESwitchingKeyCompressed<Vec<u8>>;
693    fn glwe_switching_key_compressed_alloc(
694        &self,
695        base2k: Base2K,
696        k: TorusPrecision,
697        rank_in: Rank,
698        rank_out: Rank,
699        dnum: Dnum,
700        dsize: Dsize,
701    ) -> GLWESwitchingKeyCompressed<Vec<u8>>;
702
703    fn glwe_automorphism_key_compressed_alloc_from_infos<A: GGLWEInfos>(
704        &self,
705        infos: &A,
706    ) -> GLWEAutomorphismKeyCompressed<Vec<u8>>;
707    fn glwe_automorphism_key_compressed_alloc(
708        &self,
709        base2k: Base2K,
710        k: TorusPrecision,
711        rank: Rank,
712        dnum: Dnum,
713        dsize: Dsize,
714    ) -> GLWEAutomorphismKeyCompressed<Vec<u8>>;
715
716    fn glwe_tensor_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWETensorKeyCompressed<Vec<u8>>;
717    fn glwe_tensor_key_compressed_alloc(
718        &self,
719        base2k: Base2K,
720        k: TorusPrecision,
721        rank: Rank,
722        dnum: Dnum,
723        dsize: Dsize,
724    ) -> GLWETensorKeyCompressed<Vec<u8>>;
725
726    fn glwe_to_lwe_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEToLWESwitchingKeyCompressed<Vec<u8>>;
727    fn glwe_to_lwe_key_compressed_alloc(
728        &self,
729        base2k: Base2K,
730        k: TorusPrecision,
731        rank_in: Rank,
732        dnum: Dnum,
733    ) -> GLWEToLWESwitchingKeyCompressed<Vec<u8>>;
734
735    fn lwe_to_glwe_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWEToGLWEKeyCompressed<Vec<u8>>;
736    fn lwe_to_glwe_key_compressed_alloc(
737        &self,
738        n: Degree,
739        base2k: Base2K,
740        k: TorusPrecision,
741        rank_out: Rank,
742        dnum: Dnum,
743    ) -> LWEToGLWEKeyCompressed<Vec<u8>>;
744
745    fn lwe_switching_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWESwitchingKeyCompressed<Vec<u8>>;
746    fn lwe_switching_key_compressed_alloc(
747        &self,
748        n: Degree,
749        base2k: Base2K,
750        k: TorusPrecision,
751        dnum: Dnum,
752    ) -> LWESwitchingKeyCompressed<Vec<u8>>;
753
754    fn gglwe_to_ggsw_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWEToGGSWKeyCompressed<Vec<u8>>;
755    fn gglwe_to_ggsw_key_compressed_alloc(
756        &self,
757        base2k: Base2K,
758        k: TorusPrecision,
759        rank: Rank,
760        dnum: Dnum,
761        dsize: Dsize,
762    ) -> GGLWEToGGSWKeyCompressed<Vec<u8>>;
763}
764
765impl<B: Backend> ModuleCoreCompressedAlloc for Module<B> {
766    fn glwe_compressed_alloc_from_infos<A: GLWEInfos>(&self, infos: &A) -> GLWECompressed<Vec<u8>> {
767        GLWECompressed::alloc_from_infos(infos)
768    }
769    fn glwe_compressed_alloc(&self, base2k: Base2K, k: TorusPrecision, rank: Rank) -> GLWECompressed<Vec<u8>> {
770        GLWECompressed::alloc(self.ring_degree(), base2k, k, rank)
771    }
772
773    fn lwe_compressed_alloc_from_infos<A: LWEInfos>(&self, infos: &A) -> LWECompressed<Vec<u8>> {
774        LWECompressed::alloc_from_infos(infos)
775    }
776    fn lwe_compressed_alloc(&self, base2k: Base2K, k: TorusPrecision) -> LWECompressed<Vec<u8>> {
777        LWECompressed::alloc(base2k, k)
778    }
779
780    fn gglwe_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWECompressed<Vec<u8>> {
781        GGLWECompressed::alloc_from_infos(infos)
782    }
783    fn gglwe_compressed_alloc(
784        &self,
785        base2k: Base2K,
786        k: TorusPrecision,
787        rank_in: Rank,
788        rank_out: Rank,
789        dnum: Dnum,
790        dsize: Dsize,
791    ) -> GGLWECompressed<Vec<u8>> {
792        GGLWECompressed::alloc(self.ring_degree(), base2k, k, rank_in, rank_out, dnum, dsize)
793    }
794
795    fn ggsw_compressed_alloc_from_infos<A: GGSWInfos>(&self, infos: &A) -> GGSWCompressed<Vec<u8>> {
796        GGSWCompressed::alloc_from_infos(infos)
797    }
798    fn ggsw_compressed_alloc(
799        &self,
800        base2k: Base2K,
801        k: TorusPrecision,
802        rank: Rank,
803        dnum: Dnum,
804        dsize: Dsize,
805    ) -> GGSWCompressed<Vec<u8>> {
806        GGSWCompressed::alloc(self.ring_degree(), base2k, k, rank, dnum, dsize)
807    }
808
809    fn glwe_switching_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWESwitchingKeyCompressed<Vec<u8>> {
810        GLWESwitchingKeyCompressed::alloc_from_infos(infos)
811    }
812    fn glwe_switching_key_compressed_alloc(
813        &self,
814        base2k: Base2K,
815        k: TorusPrecision,
816        rank_in: Rank,
817        rank_out: Rank,
818        dnum: Dnum,
819        dsize: Dsize,
820    ) -> GLWESwitchingKeyCompressed<Vec<u8>> {
821        GLWESwitchingKeyCompressed::alloc(self.ring_degree(), base2k, k, rank_in, rank_out, dnum, dsize)
822    }
823
824    fn glwe_automorphism_key_compressed_alloc_from_infos<A: GGLWEInfos>(
825        &self,
826        infos: &A,
827    ) -> GLWEAutomorphismKeyCompressed<Vec<u8>> {
828        GLWEAutomorphismKeyCompressed::alloc_from_infos(infos)
829    }
830    fn glwe_automorphism_key_compressed_alloc(
831        &self,
832        base2k: Base2K,
833        k: TorusPrecision,
834        rank: Rank,
835        dnum: Dnum,
836        dsize: Dsize,
837    ) -> GLWEAutomorphismKeyCompressed<Vec<u8>> {
838        GLWEAutomorphismKeyCompressed::alloc(self.ring_degree(), base2k, k, rank, dnum, dsize)
839    }
840
841    fn glwe_tensor_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWETensorKeyCompressed<Vec<u8>> {
842        GLWETensorKeyCompressed::alloc_from_infos(infos)
843    }
844    fn glwe_tensor_key_compressed_alloc(
845        &self,
846        base2k: Base2K,
847        k: TorusPrecision,
848        rank: Rank,
849        dnum: Dnum,
850        dsize: Dsize,
851    ) -> GLWETensorKeyCompressed<Vec<u8>> {
852        GLWETensorKeyCompressed::alloc(self.ring_degree(), base2k, k, rank, dnum, dsize)
853    }
854
855    fn glwe_to_lwe_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
856        GLWEToLWESwitchingKeyCompressed::alloc_from_infos(infos)
857    }
858    fn glwe_to_lwe_key_compressed_alloc(
859        &self,
860        base2k: Base2K,
861        k: TorusPrecision,
862        rank_in: Rank,
863        dnum: Dnum,
864    ) -> GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
865        GLWEToLWESwitchingKeyCompressed::alloc(self.ring_degree(), base2k, k, rank_in, dnum)
866    }
867
868    fn lwe_to_glwe_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWEToGLWEKeyCompressed<Vec<u8>> {
869        LWEToGLWEKeyCompressed::alloc_from_infos(infos)
870    }
871    fn lwe_to_glwe_key_compressed_alloc(
872        &self,
873        n: Degree,
874        base2k: Base2K,
875        k: TorusPrecision,
876        rank_out: Rank,
877        dnum: Dnum,
878    ) -> LWEToGLWEKeyCompressed<Vec<u8>> {
879        LWEToGLWEKeyCompressed::alloc(n, base2k, k, rank_out, dnum)
880    }
881
882    fn lwe_switching_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> LWESwitchingKeyCompressed<Vec<u8>> {
883        LWESwitchingKeyCompressed::alloc_from_infos(infos)
884    }
885    fn lwe_switching_key_compressed_alloc(
886        &self,
887        n: Degree,
888        base2k: Base2K,
889        k: TorusPrecision,
890        dnum: Dnum,
891    ) -> LWESwitchingKeyCompressed<Vec<u8>> {
892        LWESwitchingKeyCompressed::alloc(n, base2k, k, dnum)
893    }
894
895    fn gglwe_to_ggsw_key_compressed_alloc_from_infos<A: GGLWEInfos>(&self, infos: &A) -> GGLWEToGGSWKeyCompressed<Vec<u8>> {
896        GGLWEToGGSWKeyCompressed::alloc_from_infos(infos)
897    }
898    fn gglwe_to_ggsw_key_compressed_alloc(
899        &self,
900        base2k: Base2K,
901        k: TorusPrecision,
902        rank: Rank,
903        dnum: Dnum,
904        dsize: Dsize,
905    ) -> GGLWEToGGSWKeyCompressed<Vec<u8>> {
906        GGLWEToGGSWKeyCompressed::alloc(self.ring_degree(), base2k, k, rank, dnum, dsize)
907    }
908}
909
910/// Newtype over `u32` with arithmetic and comparisons against same type and `u32`.
911/// Arithmetic is **saturating** (add/sub/mul) to avoid debug-overflow panics.
912macro_rules! newtype_u32 {
913    ($(#[$meta:meta])* $name:ident) => {
914        $(#[$meta])*
915        #[repr(transparent)]
916        #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
917        pub struct $name(pub u32);
918
919        // ----- Conversions -----
920        impl From<$name> for u32 {
921            #[inline]
922            fn from(v: $name) -> u32 {
923                v.0
924            }
925        }
926        impl From<$name> for usize {
927            #[inline]
928            fn from(v: $name) -> usize {
929                v.0 as usize
930            }
931        }
932
933        impl From<u32> for $name {
934            #[inline]
935            fn from(v: u32) -> $name {
936                $name(v)
937            }
938        }
939        impl From<usize> for $name {
940            #[inline]
941            fn from(v: usize) -> $name {
942                debug_assert!(v <= u32::MAX as usize, "{} overflow: {v} > u32::MAX", stringify!($name));
943                $name(v as u32)
944            }
945        }
946
947        // ----- Display -----
948        impl ::core::fmt::Display for $name {
949            #[inline]
950            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
951                write!(f, "{}", self.0)
952            }
953        }
954
955        // ===== Arithmetic (same type) =====
956        impl ::core::ops::Add for $name {
957            type Output = $name;
958            #[inline]
959            fn add(self, rhs: $name) -> $name {
960                $name(self.0.saturating_add(rhs.0))
961            }
962        }
963        impl ::core::ops::Sub for $name {
964            type Output = $name;
965            #[inline]
966            fn sub(self, rhs: $name) -> $name {
967                $name(self.0.saturating_sub(rhs.0))
968            }
969        }
970        impl ::core::ops::Mul for $name {
971            type Output = $name;
972            #[inline]
973            fn mul(self, rhs: $name) -> $name {
974                $name(self.0.saturating_mul(rhs.0))
975            }
976        }
977
978        // ===== Arithmetic (with u32) =====
979        impl ::core::ops::Add<u32> for $name {
980            type Output = $name;
981            #[inline]
982            fn add(self, rhs: u32) -> $name {
983                $name(self.0.saturating_add(rhs))
984            }
985        }
986        impl ::core::ops::Sub<u32> for $name {
987            type Output = $name;
988            #[inline]
989            fn sub(self, rhs: u32) -> $name {
990                $name(self.0.saturating_sub(rhs))
991            }
992        }
993        impl ::core::ops::Mul<u32> for $name {
994            type Output = $name;
995            #[inline]
996            fn mul(self, rhs: u32) -> $name {
997                $name(self.0.saturating_mul(rhs))
998            }
999        }
1000
1001        impl $name {
1002            #[inline]
1003            pub const fn as_u32(self) -> u32 {
1004                self.0
1005            }
1006            #[inline]
1007            pub const fn as_usize(self) -> usize {
1008                self.0 as usize
1009            }
1010
1011            #[inline]
1012            pub fn div_ceil<T: Into<u32>>(self, rhs: T) -> u32 {
1013                self.0.div_ceil(rhs.into())
1014            }
1015        }
1016
1017        // Optional symmetric forms: u32 (+|-|*) $name -> $name
1018        impl ::core::ops::Add<$name> for u32 {
1019            type Output = $name;
1020            #[inline]
1021            fn add(self, rhs: $name) -> $name {
1022                $name(self.saturating_add(rhs.0))
1023            }
1024        }
1025        impl ::core::ops::Sub<$name> for u32 {
1026            type Output = $name;
1027            #[inline]
1028            fn sub(self, rhs: $name) -> $name {
1029                $name(self.saturating_sub(rhs.0))
1030            }
1031        }
1032        impl ::core::ops::Mul<$name> for u32 {
1033            type Output = $name;
1034            #[inline]
1035            fn mul(self, rhs: $name) -> $name {
1036                $name(self.saturating_mul(rhs.0))
1037            }
1038        }
1039
1040        // ===== Cross-type comparisons with u32 (both directions) =====
1041        impl ::core::cmp::PartialEq<u32> for $name {
1042            #[inline]
1043            fn eq(&self, other: &u32) -> bool {
1044                self.0 == *other
1045            }
1046        }
1047        impl ::core::cmp::PartialEq<$name> for u32 {
1048            #[inline]
1049            fn eq(&self, other: &$name) -> bool {
1050                *self == other.0
1051            }
1052        }
1053
1054        impl ::core::cmp::PartialOrd<u32> for $name {
1055            #[inline]
1056            fn partial_cmp(&self, other: &u32) -> Option<::core::cmp::Ordering> {
1057                self.0.partial_cmp(other)
1058            }
1059        }
1060        impl ::core::cmp::PartialOrd<$name> for u32 {
1061            #[inline]
1062            fn partial_cmp(&self, other: &$name) -> Option<::core::cmp::Ordering> {
1063                self.partial_cmp(&other.0)
1064            }
1065        }
1066    };
1067}
1068
1069newtype_u32!(
1070    /// Ring polynomial degree *N* (always a power of two).
1071    ///
1072    /// Wraps a [`u32`] with saturating arithmetic.
1073    Degree
1074);
1075
1076newtype_u32!(
1077    /// Torus precision in bits — the total number of significant bits
1078    /// used to represent elements of the discretised torus *T = R/Z*.
1079    ///
1080    /// Wraps a [`u32`] with saturating arithmetic.
1081    TorusPrecision
1082);
1083
1084newtype_u32!(
1085    /// Base-2 logarithm of the limb radix in the digit (CRT)
1086    /// decomposition of ciphertext coefficients.
1087    ///
1088    /// Coefficients are stored as `ceil(k / base2k)` limbs, each
1089    /// carrying `base2k` bits of precision.
1090    ///
1091    /// Wraps a [`u32`] with saturating arithmetic.
1092    Base2K
1093);
1094
1095newtype_u32!(
1096    /// Number of gadget-decomposition digits used in GGLWE / GGSW
1097    /// ciphertexts.
1098    ///
1099    /// Wraps a [`u32`] with saturating arithmetic.
1100    Dnum
1101);
1102
1103newtype_u32!(
1104    /// GLWE rank — the number of mask polynomials in a GLWE ciphertext.
1105    ///
1106    /// A rank-0 GLWE is equivalent to a plain LWE ciphertext.
1107    ///
1108    /// Wraps a [`u32`] with saturating arithmetic.
1109    Rank
1110);
1111
1112newtype_u32!(
1113    /// Size (in limbs) of each gadget-decomposition digit.
1114    ///
1115    /// Wraps a [`u32`] with saturating arithmetic.
1116    Dsize
1117);
1118
1119impl Degree {
1120    /// Returns log2(n). Assumes n is a positive power of two.
1121    pub fn log2(&self) -> usize {
1122        debug_assert!(
1123            self.0 > 0 && self.0.is_power_of_two(),
1124            "Degree::log2 requires a positive power of two, got {}",
1125            self.0
1126        );
1127        self.0.trailing_zeros() as usize
1128    }
1129}