poulpy_hal/layouts/
module.rs

1use std::{fmt::Display, marker::PhantomData, ptr::NonNull};
2
3use rand_distr::num_traits::Zero;
4
5use crate::GALOISGENERATOR;
6
7#[allow(clippy::missing_safety_doc)]
8pub trait Backend: Sized {
9    type ScalarBig: Copy + Zero + Display;
10    type ScalarPrep: Copy + Zero + Display;
11    type Handle: 'static;
12    fn layout_prep_word_count() -> usize;
13    fn layout_big_word_count() -> usize;
14    unsafe fn destroy(handle: NonNull<Self::Handle>);
15}
16
17pub struct Module<B: Backend> {
18    ptr: NonNull<B::Handle>,
19    n: u64,
20    _marker: PhantomData<B>,
21}
22
23unsafe impl<B: Backend> Sync for Module<B> {}
24unsafe impl<B: Backend> Send for Module<B> {}
25
26impl<B: Backend> Module<B> {
27    #[allow(clippy::missing_safety_doc)]
28    #[inline]
29    pub fn new_marker(n: u64) -> Self {
30        Self {
31            ptr: NonNull::dangling(),
32            n,
33            _marker: PhantomData,
34        }
35    }
36
37    #[allow(clippy::missing_safety_doc)]
38    #[inline]
39    pub unsafe fn from_nonnull(ptr: NonNull<B::Handle>, n: u64) -> Self {
40        Self {
41            ptr,
42            n,
43            _marker: PhantomData,
44        }
45    }
46
47    /// Construct from a raw pointer managed elsewhere.
48    /// SAFETY: `ptr` must be non-null and remain valid for the lifetime of this Module.
49    #[inline]
50    #[allow(clippy::missing_safety_doc)]
51    pub unsafe fn from_raw_parts(ptr: *mut B::Handle, n: u64) -> Self {
52        Self {
53            ptr: NonNull::new(ptr).expect("null module ptr"),
54            n,
55            _marker: PhantomData,
56        }
57    }
58
59    #[allow(clippy::missing_safety_doc)]
60    #[inline]
61    pub unsafe fn ptr(&self) -> *mut <B as Backend>::Handle {
62        self.ptr.as_ptr()
63    }
64
65    #[inline]
66    pub fn n(&self) -> usize {
67        self.n as usize
68    }
69    #[inline]
70    pub fn as_mut_ptr(&self) -> *mut B::Handle {
71        self.ptr.as_ptr()
72    }
73
74    #[inline]
75    pub fn log_n(&self) -> usize {
76        (usize::BITS - (self.n() - 1).leading_zeros()) as _
77    }
78
79    #[inline]
80    pub fn cyclotomic_order(&self) -> u64 {
81        (self.n() << 1) as _
82    }
83
84    // Returns GALOISGENERATOR^|generator| * sign(generator)
85    #[inline]
86    pub fn galois_element(&self, generator: i64) -> i64 {
87        if generator == 0 {
88            return 1;
89        }
90        ((mod_exp_u64(GALOISGENERATOR, generator.unsigned_abs() as usize) & (self.cyclotomic_order() - 1)) as i64)
91            * generator.signum()
92    }
93
94    // Returns gen^-1
95    #[inline]
96    pub fn galois_element_inv(&self, gal_el: i64) -> i64 {
97        if gal_el == 0 {
98            panic!("cannot invert 0")
99        }
100        ((mod_exp_u64(
101            gal_el.unsigned_abs(),
102            (self.cyclotomic_order() - 1) as usize,
103        ) & (self.cyclotomic_order() - 1)) as i64)
104            * gal_el.signum()
105    }
106}
107
108impl<B: Backend> Drop for Module<B> {
109    fn drop(&mut self) {
110        unsafe { B::destroy(self.ptr) }
111    }
112}
113
114pub fn mod_exp_u64(x: u64, e: usize) -> u64 {
115    let mut y: u64 = 1;
116    let mut x_pow: u64 = x;
117    let mut exp = e;
118    while exp > 0 {
119        if exp & 1 == 1 {
120            y = y.wrapping_mul(x_pow);
121        }
122        x_pow = x_pow.wrapping_mul(x_pow);
123        exp >>= 1;
124    }
125    y
126}