//! Polynomial ring Z[X]/(X^N+1) //! use anyhow::{Result, anyhow}; use rand::{Rng, distributions::Distribution}; use std::array; use std::fmt; use std::ops; use crate::ntt::NTT; use crate::zq::Zq; // PolynomialRing element, where the PolynomialRing is R = Z[X]/(X^n +1) #[derive(Clone, Copy, Debug)] pub struct R([i64; N]); impl From> for R { fn from(rq: crate::ringq::Rq) -> Self { Self::from_vec_u64(rq.coeffs().to_vec().iter().map(|e| e.0).collect()) } } impl R { pub fn coeffs(&self) -> [i64; N] { self.0 } pub fn to_rq(self) -> crate::Rq { crate::Rq::::from(self) } pub fn from_vec(coeffs: Vec) -> Self { let mut p = coeffs; modulus::(&mut p); Self(array::from_fn(|i| p[i])) } // this method is mostly for tests pub fn from_vec_u64(coeffs: Vec) -> Self { let coeffs_i64 = coeffs.iter().map(|c| *c as i64).collect(); Self::from_vec(coeffs_i64) } pub fn from_vec_f64(coeffs: Vec) -> Self { let coeffs_i64 = coeffs.iter().map(|c| c.round() as i64).collect(); Self::from_vec(coeffs_i64) } pub fn new(coeffs: [i64; N]) -> Self { Self(coeffs) } pub fn mul_by_i64(&self, s: i64) -> Self { Self(array::from_fn(|i| self.0[i] * s)) } // performs the multiplication and division over f64, and then it rounds the // result, only applying the mod Q at the end pub fn mul_div_round(&self, num: u64, den: u64) -> crate::Rq { let r: Vec = self .coeffs() .iter() .map(|e| ((num as f64 * *e as f64) / den as f64).round()) .collect(); crate::Rq::::from_vec_f64(r) } } pub fn mul_div_round( v: Vec, num: u64, den: u64, ) -> crate::Rq { // dbg!(&v); let r: Vec = v .iter() .map(|e| ((num as f64 * *e as f64) / den as f64).round()) .collect(); // dbg!(&r); crate::Rq::::from_vec_f64(r) } // TODO rename to make it clear that is not mod q, but mod X^N+1 // 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] = 0; } p.truncate(N); } pub fn modulus_i128(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] = 0; } p.truncate(N); } impl PartialEq for R { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl ops::Add> for R { type Output = Self; fn add(self, rhs: Self) -> Self { Self(array::from_fn(|i| self.0[i] + rhs.0[i])) } } impl ops::Add<&R> for &R { type Output = R; fn add(self, rhs: &R) -> Self::Output { R(array::from_fn(|i| self.0[i] + rhs.0[i])) } } impl ops::Sub> for R { type Output = Self; fn sub(self, rhs: Self) -> Self { Self(array::from_fn(|i| self.0[i] - rhs.0[i])) } } impl ops::Sub<&R> for &R { type Output = R; fn sub(self, rhs: &R) -> Self::Output { R(array::from_fn(|i| self.0[i] - rhs.0[i])) } } impl ops::Mul> for R { type Output = Self; fn mul(self, rhs: Self) -> Self { naive_poly_mul(&self, &rhs) } } impl ops::Mul<&R> for &R { type Output = R; fn mul(self, rhs: &R) -> Self::Output { naive_poly_mul(self, rhs) } } // TODO WIP pub fn naive_poly_mul(poly1: &R, poly2: &R) -> R { let poly1: Vec = poly1.0.iter().map(|c| *c as i128).collect(); let poly2: Vec = poly2.0.iter().map(|c| *c as i128).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)) R::::from_vec(result.iter().map(|c| *c as i64).collect()) } pub fn naive_mul(poly1: &R, poly2: &R) -> Vec { let poly1: Vec = poly1.0.iter().map(|c| *c as i128).collect(); let poly2: Vec = poly2.0.iter().map(|c| *c as i128).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]; } } modulus_i128::(&mut result); // for c_i in result.iter() { // println!("---"); // println!("{:?}", &c_i); // println!("{:?}", *c_i as i64); // println!("{:?}", (*c_i as i64) as i128); // assert_eq!(*c_i, (*c_i as i64) as i128, "{:?}", c_i); // } // let q: i128 = 65537; // let result: Vec = result // .iter() // // .map(|c_i| ((c_i % q + q) % q) as i64) // .map(|c_i| (c_i % q) as i64) // // .map(|c_i| *c_i as i64) // .collect(); // result result.iter().map(|c| *c as i64).collect() } // mul by u64 impl ops::Mul for R { type Output = Self; fn mul(self, s: u64) -> Self { self.mul_by_i64(s as i64) } } impl ops::Mul<&u64> for &R { type Output = R; fn mul(self, s: &u64) -> Self::Output { self.mul_by_i64(*s as i64) } } impl ops::Neg for R { type Output = Self; fn neg(self) -> Self::Output { Self(array::from_fn(|i| -self.0[i])) } }