You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

484 lines
12 KiB

//! Polynomial ring Z[X]/(X^N+1)
//!
use itertools::zip_eq;
use rand::{distributions::Distribution, Rng};
use std::fmt;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
// TODO rename to not have name conflicts with the Ring trait (R: Ring)
// PolynomialRing element, where the PolynomialRing is R = Z[X]/(X^n +1)
#[derive(Clone)]
pub struct R {
pub n: usize,
pub coeffs: Vec<i64>,
}
impl R {
pub fn coeffs(&self) -> Vec<i64> {
self.coeffs.clone()
}
fn zero(n: usize) -> Self {
Self {
n,
coeffs: vec![0i64; n],
}
}
fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, n: usize) -> Self {
Self {
n,
coeffs: std::iter::repeat_with(|| dist.sample(&mut rng).round() as i64)
.take(n)
.collect(),
}
}
pub fn from_vec(n: usize, coeffs: Vec<i64>) -> Self {
let mut p = coeffs;
modulus(n, &mut p);
Self { n, coeffs: p }
}
/*
// returns the decomposition of each polynomial coefficient
fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {
unimplemented!();
// array::from_fn(|i| self.coeffs[i].decompose(beta, l))
}
fn remodule<const P: u64>(&self) -> impl Ring {
unimplemented!()
}
fn mod_switch<const P: u64, const M: usize>(&self) -> R<N> {
unimplemented!()
}
/// 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 {
unimplemented!()
// fn mul_div_round<const Q: u64>(&self, num: u64, den: u64) -> crate::Rq<Q, N> {
// let r: Vec<f64> = self
// .coeffs()
// .iter()
// .map(|e| ((num as f64 * *e as f64) / den as f64).round())
// .collect();
// crate::Rq::<Q, N>::from_vec_f64(r)
}
*/
}
impl From<crate::ring_nq::Rq> for R {
fn from(rq: crate::ring_nq::Rq) -> Self {
Self::from_vec_u64(
rq.param.n,
rq.coeffs().to_vec().iter().map(|e| e.v).collect(),
)
}
}
impl R {
pub fn to_rq(self, q: u64) -> crate::Rq {
crate::Rq::from((q, self))
}
// this method is mostly for tests
pub fn from_vec_u64(n: usize, coeffs: Vec<u64>) -> Self {
let coeffs_i64 = coeffs.iter().map(|c| *c as i64).collect();
Self::from_vec(n, coeffs_i64)
}
pub fn from_vec_f64(n: usize, coeffs: Vec<f64>) -> Self {
let coeffs_i64 = coeffs.iter().map(|c| c.round() as i64).collect();
Self::from_vec(n, coeffs_i64)
}
pub fn new(n: usize, coeffs: Vec<i64>) -> Self {
assert_eq!(n, coeffs.len());
Self { n, coeffs }
}
pub fn mul_by_i64(&self, s: i64) -> Self {
Self {
n: self.n,
coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(),
}
}
pub fn infinity_norm(&self) -> u64 {
self.coeffs()
.iter()
// .map(|x| if x.0 > (Q / 2) { Q - x.0 } else { x.0 })
.map(|x| x.abs() as u64)
.fold(0, |a, b| a.max(b))
}
pub fn mod_centered_q(&self, q: u64) -> R {
let q = q as i64;
let r = self
.coeffs
.iter()
.map(|v| {
let mut res = v % q;
if res > q / 2 {
res = res - q;
}
res
})
.collect::<Vec<i64>>();
R::from_vec(self.n, r)
}
}
pub fn mul_div_round(q: u64, n: usize, v: Vec<i64>, num: u64, den: u64) -> crate::Rq {
// dbg!(&v);
let r: Vec<f64> = v
.iter()
.map(|e| ((num as f64 * *e as f64) / den as f64).round())
.collect();
// dbg!(&r);
crate::Rq::from_vec_f64(&crate::ring::RingParam { q, n }, 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(n: usize, p: &mut Vec<i64>) {
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(n: usize, p: &mut Vec<i128>) {
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.coeffs == other.coeffs && self.n == other.n
}
}
impl Add<R> for R {
type Output = Self;
fn add(self, rhs: Self) -> Self {
assert_eq!(self.n, rhs.n);
Self {
n: self.n,
coeffs: zip_eq(self.coeffs, rhs.coeffs)
.map(|(l, r)| l + r)
.collect(),
}
}
}
impl Add<&R> for &R {
type Output = R;
fn add(self, rhs: &R) -> Self::Output {
assert_eq!(self.n, rhs.n);
R {
n: self.n,
coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone())
.map(|(l, r)| l + r)
.collect(),
}
}
}
impl AddAssign for R {
fn add_assign(&mut self, rhs: Self) {
assert_eq!(self.n, rhs.n);
for i in 0..self.n {
self.coeffs[i] += rhs.coeffs[i];
}
}
}
impl Sum<R> for R {
fn sum<I>(mut iter: I) -> Self
where
I: Iterator<Item = Self>,
{
let first = iter.next().unwrap();
iter.fold(first, |acc, x| acc + x)
}
}
impl Sub<R> for R {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
assert_eq!(self.n, rhs.n);
Self {
n: self.n,
coeffs: zip_eq(self.coeffs, rhs.coeffs)
.map(|(l, r)| l - r)
.collect(),
}
}
}
impl Sub<&R> for &R {
type Output = R;
fn sub(self, rhs: &R) -> Self::Output {
assert_eq!(self.n, rhs.n);
R {
n: self.n,
coeffs: zip_eq(&self.coeffs, &rhs.coeffs)
.map(|(l, r)| l - r)
.collect(),
}
}
}
impl SubAssign for R {
fn sub_assign(&mut self, rhs: Self) {
assert_eq!(self.n, rhs.n);
for i in 0..self.n {
self.coeffs[i] -= rhs.coeffs[i];
}
}
}
impl Mul<R> for R {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
naive_poly_mul(&self, &rhs)
}
}
impl 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 {
assert_eq!(poly1.n, poly2.n);
let n = poly1.n;
let poly1: Vec<i128> = poly1.coeffs.iter().map(|c| *c as i128).collect();
let poly2: Vec<i128> = poly2.coeffs.iter().map(|c| *c as i128).collect();
let mut result: Vec<i128> = 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::<N>::from_vec(result.iter().map(|c| *c as i64).collect())
modulus_i128(n, &mut result);
// dbg!(&result);
// dbg!(R::<N>(array::from_fn(|i| result[i] as i64)).coeffs());
let result_i64: Vec<i64> = result.iter().map(|c_i| *c_i as i64).collect();
let r = R::from_vec(n, result_i64);
// sanity check: check that there are no coeffs > i64_max
assert_eq!(
result,
r.coeffs.iter().map(|c| *c as i128).collect::<Vec<_>>()
);
r
}
pub fn naive_mul_2(n: usize, poly1: &Vec<i128>, poly2: &Vec<i128>) -> Vec<i128> {
let mut result: Vec<i128> = 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::<N>::from_vec(result.iter().map(|c| *c as i64).collect())
modulus_i128(n, &mut result);
result
}
pub fn naive_mul(poly1: &R, poly2: &R) -> Vec<i64> {
assert_eq!(poly1.n, poly2.n);
let n = poly1.n;
let poly1: Vec<i128> = poly1.coeffs.iter().map(|c| *c as i128).collect();
let poly2: Vec<i128> = poly2.coeffs.iter().map(|c| *c as i128).collect();
let mut result = 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];
}
}
result.iter().map(|c| *c as i64).collect()
}
pub fn naive_mul_TMP(poly1: &R, poly2: &R) -> Vec<i64> {
assert_eq!(poly1.n, poly2.n);
let n = poly1.n;
let poly1: Vec<i128> = poly1.coeffs.iter().map(|c| *c as i128).collect();
let poly2: Vec<i128> = poly2.coeffs.iter().map(|c| *c as i128).collect();
let mut result: Vec<i128> = 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];
}
}
// dbg!(&result);
modulus_i128(n, &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);
// }
result.iter().map(|c| *c as i64).collect()
}
// wip
pub fn mod_centered_q(q: u64, n: usize, p: Vec<i128>) -> R {
let q: i128 = q as i128;
let r = p
.iter()
.map(|v| {
let mut res = v % q;
if res > q / 2 {
res = res - q;
}
res
})
.collect::<Vec<i128>>();
R::from_vec(n, r.iter().map(|v| *v as i64).collect::<Vec<i64>>())
}
impl Mul<i64> for R {
type Output = Self;
fn mul(self, s: i64) -> Self {
self.mul_by_i64(s)
}
}
// mul by u64
impl Mul<u64> for R {
type Output = Self;
fn mul(self, s: u64) -> Self {
self.mul_by_i64(s as i64)
}
}
impl Mul<&u64> for &R {
type Output = R;
fn mul(self, s: &u64) -> Self::Output {
self.mul_by_i64(*s as i64)
}
}
impl Neg for R {
type Output = Self;
fn neg(self) -> Self::Output {
// Self(array::from_fn(|i| -self.0[i]))
Self {
n: self.n,
coeffs: self.coeffs.iter().map(|c_i| -c_i).collect(),
}
}
}
impl R {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut str = "";
let mut zero = true;
for (i, coeff) in self.coeffs.iter().enumerate().rev() {
if *coeff == 0 {
continue;
}
zero = false;
f.write_str(str)?;
if *coeff != 1 {
f.write_str(coeff.to_string().as_str())?;
if i > 0 {
f.write_str("*")?;
}
}
if *coeff == 1 && i == 0 {
f.write_str(coeff.to_string().as_str())?;
}
if i == 1 {
f.write_str("x")?;
} else if i > 1 {
f.write_str("x^")?;
f.write_str(i.to_string().as_str())?;
}
str = " + ";
}
if zero {
f.write_str("0")?;
}
f.write_str(" mod Z")?;
f.write_str("/(X^")?;
f.write_str(self.n.to_string().as_str())?;
f.write_str("+1)")?;
Ok(())
}
}
impl fmt::Display for R {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt(f)?;
Ok(())
}
}
impl fmt::Debug for R {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt(f)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
#[test]
fn test_mul() -> Result<()> {
let n: usize = 2;
let q: i64 = (2u64.pow(16) + 1) as i64;
// test vectors generated with SageMath
let a: Vec<i64> = vec![q - 1, q - 1];
let b: Vec<i64> = vec![q - 1, q - 1];
let c: Vec<i64> = vec![0, 8589934592];
test_mul_opt(n, a, b, c)?;
let a: Vec<i64> = vec![1, q - 1];
let b: Vec<i64> = vec![1, q - 1];
let c: Vec<i64> = vec![-4294967295, 131072];
test_mul_opt(n, a, b, c)?;
Ok(())
}
fn test_mul_opt(n: usize, a: Vec<i64>, b: Vec<i64>, expected_c: Vec<i64>) -> Result<()> {
let mut a = R::new(n, a);
let mut b = R::new(n, b);
dbg!(&a);
dbg!(&b);
let expected_c = R::new(n, expected_c);
let mut c = naive_mul(&mut a, &mut b);
modulus(n, &mut c);
dbg!(R::from_vec(n, c.clone()));
assert_eq!(c, expected_c.coeffs);
Ok(())
}
}