poulpy_hal/layouts/
module.rs

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