arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/arith/src/ring_torus.rs
arith/src/ring_torus.rsRaw
  1//! 𝕋_<N,q>[X] = ℝ_<N,q>[X] / β„€_<N,q>[X], polynomials modulo X^N+1 with
  2//! coefficients in 𝕋_Q.
  3//!
  4//! Note: this is not an algebraic ring, since internal-product is not well
  5//! defined. But since we work over the discrete torus 𝕋_q, which we identify as
  6//! 𝕋q = β„€/qβ„€ β‰ˆ β„€q, whith q=64. Since we allow product between 𝕋q elements and
  7//! u64, we fit it into the `Ring` trait (from ring.rs) so that we can compose
  8//! the 𝕋_<N,q> implementation with the other objects from the code.
  9
 10use rand::{distributions::Distribution, Rng};
 11use std::array;
 12use std::iter::Sum;
 13use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
 14
 15use crate::{ring::Ring, torus::T64, Rq, Zq};
 16
 17/// 𝕋_<N,Q>[X] = 𝕋<Q>[X]/(X^N +1), polynomials modulo X^N+1 with coefficients in
 18/// 𝕋, where Q=2^64.
 19#[derive(Clone, Copy, Debug)]
 20pub struct Tn<const N: usize>(pub [T64; N]);
 21
 22impl<const N: usize> Ring for Tn<N> {
 23    type C = T64;
 24
 25    const Q: u64 = u64::MAX; // WIP
 26    const N: usize = N;
 27
 28    fn coeffs(&self) -> Vec<T64> {
 29        self.0.to_vec()
 30    }
 31
 32    fn zero() -> Self {
 33        Self(array::from_fn(|_| T64::zero()))
 34    }
 35
 36    fn rand(mut rng: impl Rng, dist: impl Distribution<f64>) -> Self {
 37        Self(array::from_fn(|_| T64::rand(&mut rng, &dist)))
 38    }
 39
 40    fn from_vec(coeffs: Vec<Self::C>) -> Self {
 41        let mut p = coeffs;
 42        modulus::<N>(&mut p);
 43        Self(array::from_fn(|i| p[i]))
 44    }
 45
 46    fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {
 47        let elems: Vec<Vec<T64>> = self.0.iter().map(|r| r.decompose(beta, l)).collect();
 48        // transpose it
 49        let r: Vec<Vec<T64>> = (0..elems[0].len())
 50            .map(|i| (0..elems.len()).map(|j| elems[j][i]).collect())
 51            .collect();
 52        // convert it to Tn<N>
 53        r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect()
 54    }
 55
 56    fn remodule<const P: u64>(&self) -> Tn<N> {
 57        todo!()
 58        // Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
 59    }
 60
 61    // fn mod_switch<const P: u64>(&self) -> impl Ring {
 62    fn mod_switch<const P: u64>(&self) -> Rq<P, N> {
 63        // unimplemented!()
 64        // TODO WIP
 65        let coeffs = array::from_fn(|i| Zq::<P>::from_u64(self.0[i].mod_switch::<P>().0));
 66        Rq::<P, N> {
 67            coeffs,
 68            evals: None,
 69        }
 70    }
 71
 72    /// returns [ [(num/den) * self].round() ] mod q
 73    /// ie. performs the multiplication and division over f64, and then it rounds the
 74    /// result, only applying the mod Q at the end
 75    fn mul_div_round(&self, num: u64, den: u64) -> Self {
 76        let r: Vec<T64> = self
 77            .coeffs()
 78            .iter()
 79            .map(|e| T64(((num as f64 * e.0 as f64) / den as f64).round() as u64))
 80            .collect();
 81        Self::from_vec(r)
 82    }
 83}
 84
 85impl<const N: usize> Tn<N> {
 86    // multiply self by X^-h
 87    pub fn left_rotate(&self, h: usize) -> Self {
 88        dbg!(&h);
 89        dbg!(&N);
 90        let h = h % N;
 91        assert!(h < N);
 92        let c = self.0;
 93        // c[h], c[h+1], c[h+2], ..., c[n-1],  -c[0], -c[1], ..., -c[h-1]
 94        // let r: Vec<T64> = vec![c[h..N], c[0..h].iter().map(|&c_i| -c_i).collect()].concat();
 95        dbg!(&h);
 96        let r: Vec<T64> = c[h..N]
 97            .iter()
 98            .copied()
 99            .chain(c[0..h].iter().map(|&x| -x))
100            .collect();
101        Self::from_vec(r)
102    }
103
104    pub fn from_vec_u64(v: Vec<u64>) -> Self {
105        let coeffs = v.iter().map(|c| T64(*c)).collect();
106        Self::from_vec(coeffs)
107    }
108}
109
110// apply mod (X^N+1)
111pub fn modulus<const N: usize>(p: &mut Vec<T64>) {
112    if p.len() < N {
113        return;
114    }
115    for i in N..p.len() {
116        p[i - N] = p[i - N].clone() - p[i].clone();
117        p[i] = T64::zero();
118    }
119    p.truncate(N);
120}
121
122impl<const N: usize> Add<Tn<N>> for Tn<N> {
123    type Output = Self;
124
125    fn add(self, rhs: Self) -> Self {
126        Self(array::from_fn(|i| self.0[i] + rhs.0[i]))
127    }
128}
129impl<const N: usize> Add<&Tn<N>> for &Tn<N> {
130    type Output = Tn<N>;
131
132    fn add(self, rhs: &Tn<N>) -> Self::Output {
133        Tn(array::from_fn(|i| self.0[i] + rhs.0[i]))
134    }
135}
136impl<const N: usize> AddAssign for Tn<N> {
137    fn add_assign(&mut self, rhs: Self) {
138        for i in 0..N {
139            self.0[i] += rhs.0[i];
140        }
141    }
142}
143
144impl<const N: usize> Sum<Tn<N>> for Tn<N> {
145    fn sum<I>(iter: I) -> Self
146    where
147        I: Iterator<Item = Self>,
148    {
149        let mut acc = Tn::<N>::zero();
150        for e in iter {
151            acc += e;
152        }
153        acc
154    }
155}
156
157impl<const N: usize> Sub<Tn<N>> for Tn<N> {
158    type Output = Self;
159
160    fn sub(self, rhs: Self) -> Self {
161        Self(array::from_fn(|i| self.0[i] - rhs.0[i]))
162    }
163}
164impl<const N: usize> Sub<&Tn<N>> for &Tn<N> {
165    type Output = Tn<N>;
166
167    fn sub(self, rhs: &Tn<N>) -> Self::Output {
168        Tn(array::from_fn(|i| self.0[i] - rhs.0[i]))
169    }
170}
171
172impl<const N: usize> SubAssign for Tn<N> {
173    fn sub_assign(&mut self, rhs: Self) {
174        for i in 0..N {
175            self.0[i] -= rhs.0[i];
176        }
177    }
178}
179
180impl<const N: usize> Neg for Tn<N> {
181    type Output = Self;
182
183    fn neg(self) -> Self::Output {
184        Tn(array::from_fn(|i| -self.0[i]))
185    }
186}
187
188impl<const N: usize> PartialEq for Tn<N> {
189    fn eq(&self, other: &Self) -> bool {
190        self.0 == other.0
191    }
192}
193
194impl<const N: usize> Mul<Tn<N>> for Tn<N> {
195    type Output = Self;
196
197    fn mul(self, rhs: Self) -> Self {
198        naive_poly_mul(&self, &rhs)
199    }
200}
201impl<const N: usize> Mul<&Tn<N>> for &Tn<N> {
202    type Output = Tn<N>;
203
204    fn mul(self, rhs: &Tn<N>) -> Self::Output {
205        naive_poly_mul(self, rhs)
206    }
207}
208
209fn naive_poly_mul<const N: usize>(poly1: &Tn<N>, poly2: &Tn<N>) -> Tn<N> {
210    let poly1: Vec<u128> = poly1.0.iter().map(|c| c.0 as u128).collect();
211    let poly2: Vec<u128> = poly2.0.iter().map(|c| c.0 as u128).collect();
212    let mut result: Vec<u128> = vec![0; (N * 2) - 1];
213    for i in 0..N {
214        for j in 0..N {
215            result[i + j] = result[i + j] + poly1[i] * poly2[j];
216        }
217    }
218
219    // apply mod (X^N + 1))
220    modulus_u128::<N>(&mut result);
221
222    Tn(array::from_fn(|i| T64(result[i] as u64)))
223}
224fn modulus_u128<const N: usize>(p: &mut Vec<u128>) {
225    if p.len() < N {
226        return;
227    }
228    for i in N..p.len() {
229        // p[i - N] = p[i - N].clone() - p[i].clone();
230        p[i - N] = p[i - N].wrapping_sub(p[i]);
231        p[i] = 0;
232    }
233    p.truncate(N);
234}
235
236impl<const N: usize> Mul<T64> for Tn<N> {
237    type Output = Self;
238    fn mul(self, s: T64) -> Self {
239        Self(array::from_fn(|i| self.0[i] * s))
240    }
241}
242// mul by u64
243impl<const N: usize> Mul<u64> for Tn<N> {
244    type Output = Self;
245    fn mul(self, s: u64) -> Self {
246        Self(array::from_fn(|i| self.0[i] * s))
247    }
248}
249impl<const N: usize> Mul<&u64> for &Tn<N> {
250    type Output = Tn<N>;
251    fn mul(self, s: &u64) -> Self::Output {
252        Tn::<N>(array::from_fn(|i| self.0[i] * *s))
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259
260    #[test]
261    fn test_left_rotate() {
262        const N: usize = 4;
263        let f = Tn::<N>::from_vec(
264            vec![2i64, 3, -4, -1]
265                .iter()
266                .map(|c| T64(*c as u64))
267                .collect(),
268        );
269
270        // expect f*x^-3 == -1 -2x -3x^2 +4x^3
271        assert_eq!(
272            f.left_rotate(3),
273            Tn::<N>::from_vec(
274                vec![-1i64, -2, -3, 4]
275                    .iter()
276                    .map(|c| T64(*c as u64))
277                    .collect(),
278            )
279        );
280        // expect f*x^-1 == 3 -4x -1x^2 -2x^3
281        assert_eq!(
282            f.left_rotate(1),
283            Tn::<N>::from_vec(
284                vec![3i64, -4, -1, -2]
285                    .iter()
286                    .map(|c| T64(*c as u64))
287                    .collect(),
288            )
289        );
290    }
291}