//! 𝕋_[X] = ℝ_[X] / ℤ_[X], polynomials modulo X^N+1 with //! coefficients in 𝕋_Q. //! //! Note: this is not an algebraic ring, since internal-product is not well //! defined. But since we work over the discrete torus 𝕋_q, which we identify as //! 𝕋q = ℤ/qℤ ≈ ℤq, whith q=64. Since we allow product between 𝕋q elements and //! u64, we fit it into the `Ring` trait (from ring.rs) so that we can compose //! the 𝕋_ implementation with the other objects from the code. use rand::{distributions::Distribution, Rng}; use std::array; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; use crate::{ring::Ring, torus::T64, Rq, Zq}; /// 𝕋_[X] = 𝕋[X]/(X^N +1), polynomials modulo X^N+1 with coefficients in /// 𝕋, where Q=2^64. #[derive(Clone, Copy, Debug)] pub struct Tn(pub [T64; N]); impl Ring for Tn { type C = T64; const Q: u64 = u64::MAX; // WIP const N: usize = N; fn coeffs(&self) -> Vec { self.0.to_vec() } fn zero() -> Self { Self(array::from_fn(|_| T64::zero())) } fn rand(mut rng: impl Rng, dist: impl Distribution) -> Self { Self(array::from_fn(|_| T64::rand(&mut rng, &dist))) } fn from_vec(coeffs: Vec) -> Self { let mut p = coeffs; modulus::(&mut p); Self(array::from_fn(|i| p[i])) } fn decompose(&self, beta: u32, l: u32) -> Vec { let elems: Vec> = self.0.iter().map(|r| r.decompose(beta, l)).collect(); // transpose it let r: Vec> = (0..elems[0].len()) .map(|i| (0..elems.len()).map(|j| elems[j][i]).collect()) .collect(); // convert it to Tn r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect() } fn remodule(&self) -> Tn { todo!() // Rq::::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect()) } // fn mod_switch(&self) -> impl Ring { fn mod_switch(&self) -> Rq { // unimplemented!() // TODO WIP let coeffs = array::from_fn(|i| Zq::

::from_u64(self.0[i].mod_switch::

().0)); Rq:: { coeffs, evals: None, } } /// returns [ [(num/den) * self].round() ] mod q /// ie. performs the multiplication and division over f64, and then it rounds the /// result, only applying the mod Q at the end fn mul_div_round(&self, num: u64, den: u64) -> Self { let r: Vec = self .coeffs() .iter() .map(|e| T64(((num as f64 * e.0 as f64) / den as f64).round() as u64)) .collect(); Self::from_vec(r) } } impl Tn { // multiply self by X^-h pub fn left_rotate(&self, h: usize) -> Self { dbg!(&h); dbg!(&N); let h = h % N; assert!(h < N); let c = self.0; // c[h], c[h+1], c[h+2], ..., c[n-1], -c[0], -c[1], ..., -c[h-1] // let r: Vec = vec![c[h..N], c[0..h].iter().map(|&c_i| -c_i).collect()].concat(); dbg!(&h); let r: Vec = c[h..N] .iter() .copied() .chain(c[0..h].iter().map(|&x| -x)) .collect(); Self::from_vec(r) } pub fn from_vec_u64(v: Vec) -> Self { let coeffs = v.iter().map(|c| T64(*c)).collect(); Self::from_vec(coeffs) } } // apply mod (X^N+1) pub fn modulus(p: &mut Vec) { if p.len() < N { return; } for i in N..p.len() { p[i - N] = p[i - N].clone() - p[i].clone(); p[i] = T64::zero(); } p.truncate(N); } impl Add> for Tn { type Output = Self; fn add(self, rhs: Self) -> Self { Self(array::from_fn(|i| self.0[i] + rhs.0[i])) } } impl Add<&Tn> for &Tn { type Output = Tn; fn add(self, rhs: &Tn) -> Self::Output { Tn(array::from_fn(|i| self.0[i] + rhs.0[i])) } } impl AddAssign for Tn { fn add_assign(&mut self, rhs: Self) { for i in 0..N { self.0[i] += rhs.0[i]; } } } impl Sum> for Tn { fn sum(iter: I) -> Self where I: Iterator, { let mut acc = Tn::::zero(); for e in iter { acc += e; } acc } } impl Sub> for Tn { type Output = Self; fn sub(self, rhs: Self) -> Self { Self(array::from_fn(|i| self.0[i] - rhs.0[i])) } } impl Sub<&Tn> for &Tn { type Output = Tn; fn sub(self, rhs: &Tn) -> Self::Output { Tn(array::from_fn(|i| self.0[i] - rhs.0[i])) } } impl SubAssign for Tn { fn sub_assign(&mut self, rhs: Self) { for i in 0..N { self.0[i] -= rhs.0[i]; } } } impl Neg for Tn { type Output = Self; fn neg(self) -> Self::Output { Tn(array::from_fn(|i| -self.0[i])) } } impl PartialEq for Tn { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl Mul> for Tn { type Output = Self; fn mul(self, rhs: Self) -> Self { naive_poly_mul(&self, &rhs) } } impl Mul<&Tn> for &Tn { type Output = Tn; fn mul(self, rhs: &Tn) -> Self::Output { naive_poly_mul(self, rhs) } } fn naive_poly_mul(poly1: &Tn, poly2: &Tn) -> Tn { let poly1: Vec = poly1.0.iter().map(|c| c.0 as u128).collect(); let poly2: Vec = poly2.0.iter().map(|c| c.0 as u128).collect(); let mut result: Vec = vec![0; (N * 2) - 1]; for i in 0..N { for j in 0..N { result[i + j] = result[i + j] + poly1[i] * poly2[j]; } } // apply mod (X^N + 1)) modulus_u128::(&mut result); Tn(array::from_fn(|i| T64(result[i] as u64))) } fn modulus_u128(p: &mut Vec) { if p.len() < N { return; } for i in N..p.len() { // p[i - N] = p[i - N].clone() - p[i].clone(); p[i - N] = p[i - N].wrapping_sub(p[i]); p[i] = 0; } p.truncate(N); } impl Mul for Tn { type Output = Self; fn mul(self, s: T64) -> Self { Self(array::from_fn(|i| self.0[i] * s)) } } // mul by u64 impl Mul for Tn { type Output = Self; fn mul(self, s: u64) -> Self { Self(array::from_fn(|i| self.0[i] * s)) } } impl Mul<&u64> for &Tn { type Output = Tn; fn mul(self, s: &u64) -> Self::Output { Tn::(array::from_fn(|i| self.0[i] * *s)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_left_rotate() { const N: usize = 4; let f = Tn::::from_vec( vec![2i64, 3, -4, -1] .iter() .map(|c| T64(*c as u64)) .collect(), ); // expect f*x^-3 == -1 -2x -3x^2 +4x^3 assert_eq!( f.left_rotate(3), Tn::::from_vec( vec![-1i64, -2, -3, 4] .iter() .map(|c| T64(*c as u64)) .collect(), ) ); // expect f*x^-1 == 3 -4x -1x^2 -2x^3 assert_eq!( f.left_rotate(1), Tn::::from_vec( vec![3i64, -4, -1, -2] .iter() .map(|c| T64(*c as u64)) .collect(), ) ); } }