From d60eb1dff11c0d11899941b09d11392cceb8ef8a Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 22 Jul 2025 06:22:06 +0000 Subject: [PATCH] =?UTF-8?q?add=20discretized=20torus=20&=20=F0=9D=95=8B=5F?= =?UTF-8?q?[X];=20organize=20a=20bit=20arith=20crate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arith/src/lib.rs | 30 +- arith/src/naive_ntt.rs | 4 +- arith/src/ring.rs | 460 ++--------------------------- arith/src/ring_n.rs | 434 +++++++++++++++++++++++++++ arith/src/{ringq.rs => ring_nq.rs} | 34 ++- arith/src/ring_torus.rs | 192 ++++++++++++ arith/src/torus.rs | 142 +++++++++ arith/src/traits.rs | 32 -- arith/src/zq.rs | 72 ++++- bfv/src/lib.rs | 28 +- ckks/src/encoder.rs | 2 +- ckks/src/lib.rs | 6 +- 12 files changed, 913 insertions(+), 523 deletions(-) create mode 100644 arith/src/ring_n.rs rename arith/src/{ringq.rs => ring_nq.rs} (98%) create mode 100644 arith/src/ring_torus.rs create mode 100644 arith/src/torus.rs delete mode 100644 arith/src/traits.rs diff --git a/arith/src/lib.rs b/arith/src/lib.rs index 0350857..f04c61b 100644 --- a/arith/src/lib.rs +++ b/arith/src/lib.rs @@ -6,19 +6,29 @@ pub mod complex; pub mod matrix; -mod naive_ntt; // note: for dev only -pub mod ntt; +pub mod torus; +pub mod zq; + pub mod ring; -pub mod ringq; -pub mod traits; +pub mod ring_n; +pub mod ring_nq; +pub mod ring_torus; pub mod tuple_ring; -pub mod zq; + +mod naive_ntt; // note: for dev only +pub mod ntt; + +// expose objects pub use complex::C; pub use matrix::Matrix; -pub use ntt::NTT; -pub use ring::R; -pub use ringq::Rq; -pub use traits::Ring; -pub use tuple_ring::TR; +pub use torus::T64; pub use zq::Zq; + +pub use ring::Ring; +pub use ring_n::R; +pub use ring_nq::Rq; +pub use ring_torus::Tn; +pub use tuple_ring::TR; + +pub use ntt::NTT; diff --git a/arith/src/naive_ntt.rs b/arith/src/naive_ntt.rs index 07f6487..231ea1a 100644 --- a/arith/src/naive_ntt.rs +++ b/arith/src/naive_ntt.rs @@ -106,8 +106,8 @@ mod tests { use super::*; use rand_distr::Uniform; - use crate::ringq::matrix_vec_product; - use crate::ringq::Rq; + use crate::ring_nq::matrix_vec_product; + use crate::ring_nq::Rq; #[test] fn roots_of_unity() -> Result<()> { diff --git a/arith/src/ring.rs b/arith/src/ring.rs index 66a4da3..03205ac 100644 --- a/arith/src/ring.rs +++ b/arith/src/ring.rs @@ -1,433 +1,33 @@ -//! Polynomial ring Z[X]/(X^N+1) -//! - -use anyhow::Result; use rand::{distributions::Distribution, Rng}; -use std::array; -use std::fmt; +use std::fmt::Debug; use std::iter::Sum; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use crate::Ring; - -// 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, Copy)] -pub struct R(pub [i64; N]); - -impl Ring for R { - type C = i64; - fn coeffs(&self) -> Vec { - self.0.to_vec() - } - fn zero() -> Self { - let coeffs: [i64; N] = array::from_fn(|_| 0i64); - Self(coeffs) - } - fn rand(mut rng: impl Rng, dist: impl Distribution) -> Self { - // let coeffs: [i64; N] = array::from_fn(|_| Self::C::rand(&mut rng, &dist)); - let coeffs: [i64; N] = array::from_fn(|_| dist.sample(&mut rng).round() as i64); - Self(coeffs) - // let coeffs: [C; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng))); - // Self(coeffs) - } - - // returns the decomposition of each polynomial coefficient - fn decompose(&self, beta: u32, l: u32) -> Vec { - unimplemented!(); - // array::from_fn(|i| self.coeffs[i].decompose(beta, l)) - } -} - -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 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) -> R { - let q = Q as i64; - let r = self - .0 - .iter() - .map(|v| { - let mut res = v % q; - if res > q / 2 { - res = res - q; - } - res - }) - .collect::>(); - R::::from_vec(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 Add> for R { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Self(array::from_fn(|i| self.0[i] + rhs.0[i])) - } -} -impl 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 AddAssign for R { - fn add_assign(&mut self, rhs: Self) { - for i in 0..N { - self.0[i] += rhs.0[i]; - } - } -} - -impl Sum> for R { - fn sum(iter: I) -> Self - where - I: Iterator, - { - let mut acc = R::::zero(); - for e in iter { - acc += e; - } - acc - } -} - -impl Sub> for R { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - Self(array::from_fn(|i| self.0[i] - rhs.0[i])) - } -} -impl 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 SubAssign for R { - fn sub_assign(&mut self, rhs: Self) { - for i in 0..N { - self.0[i] -= rhs.0[i]; - } - } -} - -impl Mul> 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 { - 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()) - modulus_i128::(&mut result); - // dbg!(&result); - // dbg!(R::(array::from_fn(|i| result[i] as i64)).coeffs()); - - // sanity check: check that there are no coeffs > i64_max - assert_eq!( - result, - R::(array::from_fn(|i| result[i] as i64)) - .coeffs() - .iter() - .map(|c| *c as i128) - .collect::>() - ); - R(array::from_fn(|i| result[i] as i64)) -} -pub fn naive_mul_2(poly1: &Vec, poly2: &Vec) -> Vec { - 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()) - modulus_i128::(&mut result); - result -} - -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![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 { - 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]; - } - } - - // dbg!(&result); - 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); - // } - result.iter().map(|c| *c as i64).collect() -} - -// wip -pub fn mod_centered_q(p: Vec) -> 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::>(); - R::::from_vec(r.iter().map(|v| *v as i64).collect::>()) -} - -// mul by u64 -impl Mul 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])) - } -} - -impl R { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut str = ""; - let mut zero = true; - for (i, coeff) in self.0.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(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<()> { - const Q: u64 = 2u64.pow(16) + 1; - const N: usize = 2; - let q: i64 = Q as i64; - - // test vectors generated with SageMath - let a: [i64; N] = [q - 1, q - 1]; - let b: [i64; N] = [q - 1, q - 1]; - let c: [i64; N] = [0, 8589934592]; - test_mul_opt::(a, b, c)?; - - let a: [i64; N] = [1, q - 1]; - let b: [i64; N] = [1, q - 1]; - let c: [i64; N] = [-4294967295, 131072]; - test_mul_opt::(a, b, c)?; - - Ok(()) - } - fn test_mul_opt( - a: [i64; N], - b: [i64; N], - expected_c: [i64; N], - ) -> Result<()> { - let mut a = R::new(a); - let mut b = R::new(b); - dbg!(&a); - dbg!(&b); - let expected_c = R::new(expected_c); - - let mut c = naive_mul(&mut a, &mut b); - modulus::(&mut c); - dbg!(R::::from_vec(c.clone())); - assert_eq!(c, expected_c.0.to_vec()); - Ok(()) - } +use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; + +/// Represents a ring element. Currently implemented by ring_n.rs#R and ring_nq.rs#Rq. +pub trait Ring: + Sized + + Add + + AddAssign + + Sum + + Sub + + SubAssign + + Mul + + Mul // scalar mul + + PartialEq + + Debug + + Clone + + Sum<::Output> + + Sum<::Output> +{ + /// C defines the coefficient type + type C: Debug + Clone; + + fn coeffs(&self) -> Vec; + fn zero() -> Self; + // note/wip/warning: dist (0,q) with f64, will output more '0=q' elements than other values + fn rand(rng: impl Rng, dist: impl Distribution) -> Self; + + fn from_vec(coeffs: Vec) -> Self; + + fn decompose(&self, beta: u32, l: u32) -> Vec; } diff --git a/arith/src/ring_n.rs b/arith/src/ring_n.rs new file mode 100644 index 0000000..a2cbef4 --- /dev/null +++ b/arith/src/ring_n.rs @@ -0,0 +1,434 @@ +//! Polynomial ring Z[X]/(X^N+1) +//! + +use anyhow::Result; +use rand::{distributions::Distribution, Rng}; +use std::array; +use std::fmt; +use std::iter::Sum; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use crate::Ring; + +// 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, Copy)] +pub struct R(pub [i64; N]); + +impl Ring for R { + type C = i64; + fn coeffs(&self) -> Vec { + self.0.to_vec() + } + fn zero() -> Self { + let coeffs: [i64; N] = array::from_fn(|_| 0i64); + Self(coeffs) + } + fn rand(mut rng: impl Rng, dist: impl Distribution) -> Self { + // let coeffs: [i64; N] = array::from_fn(|_| Self::C::rand(&mut rng, &dist)); + let coeffs: [i64; N] = array::from_fn(|_| dist.sample(&mut rng).round() as i64); + Self(coeffs) + // let coeffs: [C; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng))); + // Self(coeffs) + } + + fn from_vec(coeffs: Vec) -> Self { + let mut p = coeffs; + modulus::(&mut p); + Self(array::from_fn(|i| p[i])) + } + + // returns the decomposition of each polynomial coefficient + fn decompose(&self, beta: u32, l: u32) -> Vec { + unimplemented!(); + // array::from_fn(|i| self.coeffs[i].decompose(beta, l)) + } +} + +impl From> for R { + fn from(rq: crate::ring_nq::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) + } + + // 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 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) -> R { + let q = Q as i64; + let r = self + .0 + .iter() + .map(|v| { + let mut res = v % q; + if res > q / 2 { + res = res - q; + } + res + }) + .collect::>(); + R::::from_vec(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 Add> for R { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(array::from_fn(|i| self.0[i] + rhs.0[i])) + } +} +impl 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 AddAssign for R { + fn add_assign(&mut self, rhs: Self) { + for i in 0..N { + self.0[i] += rhs.0[i]; + } + } +} + +impl Sum> for R { + fn sum(iter: I) -> Self + where + I: Iterator, + { + let mut acc = R::::zero(); + for e in iter { + acc += e; + } + acc + } +} + +impl Sub> for R { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Self(array::from_fn(|i| self.0[i] - rhs.0[i])) + } +} +impl 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 SubAssign for R { + fn sub_assign(&mut self, rhs: Self) { + for i in 0..N { + self.0[i] -= rhs.0[i]; + } + } +} + +impl Mul> 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 { + 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()) + modulus_i128::(&mut result); + // dbg!(&result); + // dbg!(R::(array::from_fn(|i| result[i] as i64)).coeffs()); + + // sanity check: check that there are no coeffs > i64_max + assert_eq!( + result, + R::(array::from_fn(|i| result[i] as i64)) + .coeffs() + .iter() + .map(|c| *c as i128) + .collect::>() + ); + R(array::from_fn(|i| result[i] as i64)) +} +pub fn naive_mul_2(poly1: &Vec, poly2: &Vec) -> Vec { + 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()) + modulus_i128::(&mut result); + result +} + +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![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 { + 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]; + } + } + + // dbg!(&result); + 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); + // } + result.iter().map(|c| *c as i64).collect() +} + +// wip +pub fn mod_centered_q(p: Vec) -> 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::>(); + R::::from_vec(r.iter().map(|v| *v as i64).collect::>()) +} + +// mul by u64 +impl Mul 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])) + } +} + +impl R { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut str = ""; + let mut zero = true; + for (i, coeff) in self.0.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(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<()> { + const Q: u64 = 2u64.pow(16) + 1; + const N: usize = 2; + let q: i64 = Q as i64; + + // test vectors generated with SageMath + let a: [i64; N] = [q - 1, q - 1]; + let b: [i64; N] = [q - 1, q - 1]; + let c: [i64; N] = [0, 8589934592]; + test_mul_opt::(a, b, c)?; + + let a: [i64; N] = [1, q - 1]; + let b: [i64; N] = [1, q - 1]; + let c: [i64; N] = [-4294967295, 131072]; + test_mul_opt::(a, b, c)?; + + Ok(()) + } + fn test_mul_opt( + a: [i64; N], + b: [i64; N], + expected_c: [i64; N], + ) -> Result<()> { + let mut a = R::new(a); + let mut b = R::new(b); + dbg!(&a); + dbg!(&b); + let expected_c = R::new(expected_c); + + let mut c = naive_mul(&mut a, &mut b); + modulus::(&mut c); + dbg!(R::::from_vec(c.clone())); + assert_eq!(c, expected_c.0.to_vec()); + Ok(()) + } +} diff --git a/arith/src/ringq.rs b/arith/src/ring_nq.rs similarity index 98% rename from arith/src/ringq.rs rename to arith/src/ring_nq.rs index c8478dd..4c6b1c0 100644 --- a/arith/src/ringq.rs +++ b/arith/src/ring_nq.rs @@ -1,6 +1,7 @@ //! Polynomial ring Z_q[X]/(X^N+1) //! +use anyhow::{anyhow, Result}; use rand::{distributions::Distribution, Rng}; use std::array; use std::fmt; @@ -9,7 +10,6 @@ use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; use crate::ntt::NTT; use crate::zq::{modulus_u64, Zq}; -use anyhow::{anyhow, Result}; use crate::Ring; @@ -29,6 +29,7 @@ pub struct Rq { impl Ring for Rq { type C = Zq; + fn coeffs(&self) -> Vec { self.coeffs.to_vec() } @@ -48,6 +49,16 @@ impl Ring for Rq { } } + fn from_vec(coeffs: Vec>) -> Self { + let mut p = coeffs; + modulus::(&mut p); + let coeffs = array::from_fn(|i| p[i]); + Self { + coeffs, + evals: None, + } + } + // returns the decomposition of each polynomial coefficient, such // decomposition will be a vecotor of length N, containint N vectors of Zq fn decompose(&self, beta: u32, l: u32) -> Vec { @@ -61,8 +72,8 @@ impl Ring for Rq { } } -impl From> for Rq { - fn from(r: crate::ring::R) -> Self { +impl From> for Rq { + fn from(r: crate::ring_n::R) -> Self { Self::from_vec( r.coeffs() .iter() @@ -104,15 +115,6 @@ impl Rq { // evals: None, // } // } - pub fn from_vec(coeffs: Vec>) -> Self { - let mut p = coeffs; - modulus::(&mut p); - let coeffs = array::from_fn(|i| p[i]); - Self { - coeffs, - evals: None, - } - } // this method is mostly for tests pub fn from_vec_u64(coeffs: Vec) -> Self { let coeffs_mod_q = coeffs.iter().map(|c| Zq::from_u64(*c)).collect(); @@ -286,7 +288,7 @@ impl Rq { .map(|x| if x.0 > (Q / 2) { Q - x.0 } else { x.0 }) .fold(0, |a, b| a.max(b)) } - pub fn mod_centered_q(&self) -> crate::ring::R { + pub fn mod_centered_q(&self) -> crate::ring_n::R { self.to_r().mod_centered_q::() } } @@ -535,7 +537,7 @@ mod tests { use super::*; #[test] - fn poly_ring() { + fn test_polynomial_ring() { // the test values used are generated with SageMath const Q: u64 = 7; const N: usize = 3; @@ -623,14 +625,14 @@ mod tests { let d = a.decompose(beta, l); assert_eq!( - d[0], + d[0].coeffs().to_vec(), vec![1u64, 3, 0, 1] .iter() .map(|e| Zq::::from_u64(*e)) .collect::>() ); assert_eq!( - d[1], + d[1].coeffs().to_vec(), vec![3u64, 2, 3, 2] .iter() .map(|e| Zq::::from_u64(*e)) diff --git a/arith/src/ring_torus.rs b/arith/src/ring_torus.rs new file mode 100644 index 0000000..41c7b10 --- /dev/null +++ b/arith/src/ring_torus.rs @@ -0,0 +1,192 @@ +//! 𝕋_[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, Sub, SubAssign}; + +use crate::{ring::Ring, torus::T64}; + +/// 𝕋_[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; + + 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_f64(&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() + } +} + +// 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 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); + + // sanity check: check that there are no coeffs > i64_max + assert_eq!( + result, + Tn::(array::from_fn(|i| T64(result[i] as u64))) + .coeffs() + .iter() + .map(|c| c.0 as u128) + .collect::>() + ); + 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] = 0; + } + p.truncate(N); +} + +// 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)) + } +} diff --git a/arith/src/torus.rs b/arith/src/torus.rs new file mode 100644 index 0000000..0c6abb2 --- /dev/null +++ b/arith/src/torus.rs @@ -0,0 +1,142 @@ +use rand::{distributions::Distribution, Rng}; +use std::{ + iter::Sum, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; + +/// Let 𝕋 = ℝ/ℤ, where 𝕋 is a ℤ-module, with homogeneous external product. +/// Let 𝕋q +/// T64 is 𝕋q with q=2^Ω, with Ω=64. We identify 𝕋q=(1/q)ℤ/ℤ ≈ ℤq. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct T64(pub u64); + +impl T64 { + pub fn zero() -> Self { + Self(0u64) + } + pub fn rand(mut rng: impl Rng, dist: impl Distribution) -> Self { + let r: u64 = dist.sample(&mut rng); + Self(r) + } + pub fn rand_f64(mut rng: impl Rng, dist: impl Distribution) -> Self { + let r: f64 = dist.sample(&mut rng); + Self(r.round() as u64) + } + + /// Note: only beta=2 and l=64 is supported. + pub fn decompose(&self, beta: u32, l: u32) -> Vec { + assert_eq!(beta, 2u32); // only beta=2 supported + assert_eq!(l, 64u32); // only l=64 supported + + (0..64) + .rev() + .map(|i| T64(((self.0 >> i) & 1) as u64)) + .collect() + } +} + +impl Add for T64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_add(rhs.0)) + } +} +impl AddAssign for T64 { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl Sub for T64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_sub(rhs.0)) + } +} +impl SubAssign for T64 { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} + +impl Neg for T64 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(self.0.wrapping_neg()) + } +} + +impl Sum for T64 { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self(0), |acc, x| acc + x) + } +} + +impl Mul for T64 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_mul(rhs.0)) + } +} +impl MulAssign for T64 { + fn mul_assign(&mut self, rhs: Self) { + self.0 *= rhs.0; + } +} + +// mul by u64 +impl Mul for T64 { + type Output = Self; + + fn mul(self, s: u64) -> Self { + Self(self.0 * s) + } +} +impl Mul<&u64> for &T64 { + type Output = T64; + + fn mul(self, s: &u64) -> Self::Output { + T64(self.0 * s) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::distributions::Standard; + + fn recompose(d: Vec) -> T64 { + T64(d.iter().fold(0u64, |acc, &b| (acc << 1) | b.0)) + } + #[test] + fn test_decompose() { + let beta: u32 = 2; + let l: u32 = 64; + + let x = T64(12345); + let d = x.decompose(beta, l); + assert_eq!(recompose(d), T64(12345)); + + let x = T64(0); + let d = x.decompose(beta, l); + assert_eq!(recompose(d), T64(0)); + + let x = T64(u64::MAX - 1); + let d = x.decompose(beta, l); + assert_eq!(recompose(d), T64(u64::MAX - 1)); + + let mut rng = rand::thread_rng(); + for _ in 0..1000 { + let x = T64::rand(&mut rng, Standard); + let d = x.decompose(beta, l); + assert_eq!(recompose(d), x); + } + } +} diff --git a/arith/src/traits.rs b/arith/src/traits.rs deleted file mode 100644 index 2dbb984..0000000 --- a/arith/src/traits.rs +++ /dev/null @@ -1,32 +0,0 @@ -use anyhow::Result; -use rand::{distributions::Distribution, Rng}; -use std::fmt::Debug; -use std::iter::Sum; -use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; - -/// Represents a ring element. Currently implemented by ring.rs#R and ringq.rs#Rq. -pub trait Ring: - Sized - + Add - + AddAssign - + Sum - + Sub - + SubAssign - + Mul - + Mul // scalar mul - + PartialEq - + Debug - + Clone - + Sum<::Output> - + Sum<::Output> -{ - /// C defines the coefficient type - type C: Debug + Clone; - - fn coeffs(&self) -> Vec; - fn zero() -> Self; - // note/wip/warning: dist (0,q) with f64, will output more '0=q' elements than other values - fn rand(rng: impl Rng, dist: impl Distribution) -> Self; - - fn decompose(&self, beta: u32, l: u32) -> Vec; -} diff --git a/arith/src/zq.rs b/arith/src/zq.rs index 0d2e360..3f42887 100644 --- a/arith/src/zq.rs +++ b/arith/src/zq.rs @@ -1,7 +1,6 @@ -use anyhow::{anyhow, Result}; use rand::{distributions::Distribution, Rng}; use std::fmt; -use std::ops; +use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; /// Z_q, integers modulus q, not necessarily prime #[derive(Clone, Copy, PartialEq)] @@ -59,7 +58,7 @@ impl Zq { } } pub fn zero() -> Self { - Zq(0u64) + Self(0u64) } pub fn square(self) -> Self { self * self @@ -131,8 +130,14 @@ impl Zq { Zq::::from_u64(((self.0 as f64 * Q2 as f64) / Q as f64).round() as u64) } - // TODO more efficient method for when decomposing with base 2 (beta=2) pub fn decompose(&self, beta: u32, l: u32) -> Vec { + if beta == 2 { + self.decompose_base2(l) + } else { + self.decompose_base_beta(beta, l) + } + } + pub fn decompose_base_beta(&self, beta: u32, l: u32) -> Vec { let mut rem: u64 = self.0; // next if is for cases in which beta does not divide Q (concretely // beta^l!=Q). round to the nearest multiple of q/beta^l @@ -152,6 +157,41 @@ impl Zq { } x } + /// decompose when beta=2 + pub fn decompose_base2(&self, l: u32) -> Vec { + // next if is for cases in which beta does not divide Q (concretely + // beta^l!=Q). round to the nearest multiple of q/beta^l + if self.0 >= 1 << l as u64 { + // rem = Q - 1 - (Q / beta as u64); // floor + // (where beta=2) + return vec![Zq(1); l as usize]; + } + + (0..l) + .rev() + .map(|i| Self(((self.0 >> i) & 1) as u64)) + .collect() + + // naive ver: + // let mut rem: u64 = self.0; + // // next if is for cases in which beta does not divide Q (concretely + // // beta^l!=Q). round to the nearest multiple of q/beta^l + // if rem >= 1 << l as u64 { + // // rem = Q - 1 - (Q / beta as u64); // floor + // return vec![Zq(1); l as usize]; + // } + // + // let mut x: Vec = vec![]; + // for i in 1..l + 1 { + // let den = Q / (1 << i as u64); + // let x_i = rem / den; // division between u64 already does floor + // x.push(Self::from_u64(x_i)); + // if x_i != 0 { + // rem = rem % den; + // } + // } + // x + } } impl Zq { @@ -163,7 +203,7 @@ impl Zq { } } -impl ops::Add> for Zq { +impl Add> for Zq { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -174,7 +214,7 @@ impl ops::Add> for Zq { Zq(r) } } -impl ops::Add<&Zq> for &Zq { +impl Add<&Zq> for &Zq { type Output = Zq; fn add(self, rhs: &Zq) -> Self::Output { @@ -185,7 +225,7 @@ impl ops::Add<&Zq> for &Zq { Zq(r) } } -impl ops::AddAssign> for Zq { +impl AddAssign> for Zq { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs } @@ -198,7 +238,7 @@ impl std::iter::Sum for Zq { iter.fold(Zq(0), |acc, x| acc + x) } } -impl ops::Sub> for Zq { +impl Sub> for Zq { type Output = Self; fn sub(self, rhs: Self) -> Zq { @@ -209,7 +249,7 @@ impl ops::Sub> for Zq { } } } -impl ops::Sub<&Zq> for &Zq { +impl Sub<&Zq> for &Zq { type Output = Zq; fn sub(self, rhs: &Zq) -> Self::Output { @@ -220,19 +260,19 @@ impl ops::Sub<&Zq> for &Zq { } } } -impl ops::SubAssign> for Zq { +impl SubAssign> for Zq { fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs } } -impl ops::Neg for Zq { +impl Neg for Zq { type Output = Self; fn neg(self) -> Self::Output { Zq(Q - self.0) } } -impl ops::Mul> for Zq { +impl Mul> for Zq { type Output = Self; fn mul(self, rhs: Self) -> Zq { @@ -241,7 +281,7 @@ impl ops::Mul> for Zq { // Zq((self.0 * rhs.0) % Q) } } -impl ops::Div> for Zq { +impl Div> for Zq { type Output = Self; fn div(self, rhs: Self) -> Zq { @@ -313,9 +353,8 @@ mod tests { for _ in 0..1000 { let x = Zq::::from_u64(dist.sample(&mut rng)); - let d = x.decompose(beta, l); - + assert_eq!(d.len(), l as usize); assert_eq!(recompose::(beta, l, d), x); } } @@ -327,6 +366,7 @@ mod tests { let l: u32 = 4; let x = Zq::::from_u64(16); // in q, but bigger than beta^l let d = x.decompose(beta, l); + assert_eq!(d.len(), l as usize); assert_eq!(recompose::(beta, l, d), Zq(15)); const Q2: u64 = 5u64.pow(3) + 1; @@ -334,6 +374,7 @@ mod tests { let l: u32 = 3; let x = Zq::::from_u64(125); // in q, but bigger than beta^l let d = x.decompose(beta, l); + assert_eq!(d.len(), l as usize); assert_eq!(recompose::(beta, l, d), Zq(124)); const Q3: u64 = 2u64.pow(16) + 1; @@ -341,6 +382,7 @@ mod tests { let l: u32 = 16; let x = Zq::::from_u64(Q3 - 1); // in q, but bigger than beta^l let d = x.decompose(beta, l); + assert_eq!(d.len(), l as usize); assert_eq!(recompose::(beta, l, d), Zq(beta.pow(l) as u64 - 1)); } } diff --git a/bfv/src/lib.rs b/bfv/src/lib.rs index d934105..8c43341 100644 --- a/bfv/src/lib.rs +++ b/bfv/src/lib.rs @@ -52,7 +52,7 @@ impl RLWE { // tensor (\in R) (2021-204 p.9) // NOTE: here can use *, but at first versions want to make it explicit // that we're using the naive mul. TODO use *. - use arith::ring::naive_mul; + use arith::ring_n::naive_mul; let c0: Vec = naive_mul(&a0, &b0); let c1_l: Vec = naive_mul(&a0, &b1); let c1_r = naive_mul(&a1, &b0); @@ -60,9 +60,9 @@ impl RLWE { let c2: Vec = naive_mul(&a1, &b1); // scale down, then reduce module Q, so result is \in R_q - let c0: Rq = arith::ring::mul_div_round::(c0, T, Q); - let c1: Rq = arith::ring::mul_div_round::(c1, T, Q); - let c2: Rq = arith::ring::mul_div_round::(c2, T, Q); + let c0: Rq = arith::ring_n::mul_div_round::(c0, T, Q); + let c1: Rq = arith::ring_n::mul_div_round::(c1, T, Q); + let c2: Rq = arith::ring_n::mul_div_round::(c2, T, Q); (c0, c1, c2) } @@ -72,9 +72,9 @@ impl RLWE { BFV::::relinearize_204::(&rlk, &c0, &c1, &c2) } } -// naive mul in the ring Rq, reusing the ring::naive_mul and then applying mod(X^N +1) +// naive mul in the ring Rq, reusing the ring_n::naive_mul and then applying mod(X^N +1) fn tmp_naive_mul(a: Rq, b: Rq) -> Rq { - Rq::::from_vec_i64(arith::ring::naive_mul(&a.to_r(), &b.to_r())) + Rq::::from_vec_i64(arith::ring_n::naive_mul(&a.to_r(), &b.to_r())) } impl ops::Add> for RLWE { @@ -139,7 +139,7 @@ impl BFV { let cs = c.0 + c.1 * sk.0; // done in mod q // same but with naive_mul: - // let c1s = arith::ring::naive_mul(&c.1.to_r(), &sk.0.to_r()); + // let c1s = arith::ring_n::naive_mul(&c.1.to_r(), &sk.0.to_r()); // let c1s = Rq::::from_vec_i64(c1s); // let cs = c.0 + c1s; @@ -219,11 +219,11 @@ impl BFV { // let r0: Rq = c2rlk0.mul_div_round(1, P).remodule::(); // let r1: Rq = c2rlk1.mul_div_round(1, P).remodule::(); - use arith::ring::naive_mul; + use arith::ring_n::naive_mul; let c2rlk0: Vec = naive_mul(&c2.to_r(), &rlk.0.to_r()); let c2rlk1: Vec = naive_mul(&c2.to_r(), &rlk.1.to_r()); - let r0: Rq = arith::ring::mul_div_round::(c2rlk0, 1, P); - let r1: Rq = arith::ring::mul_div_round::(c2rlk1, 1, P); + let r0: Rq = arith::ring_n::mul_div_round::(c2rlk0, 1, P); + let r1: Rq = arith::ring_n::mul_div_round::(c2rlk1, 1, P); let res = RLWE::(c0 + &r0, c1 + &r1); res @@ -294,7 +294,7 @@ mod tests { fn test_constant_add_mul() -> Result<()> { const Q: u64 = 2u64.pow(16) + 1; const N: usize = 16; - const T: u64 = 16; // plaintext modulus + const T: u64 = 8; // plaintext modulus type S = BFV; let mut rng = rand::thread_rng(); @@ -488,10 +488,10 @@ mod tests { // decrypt non-relinearized mul result let m3: Rq = c_a + c_b * sk.0 + c_c * sk.0 * sk.0; // let m3: Rq = c_a - // + Rq::::from_vec_i64(arith::ring::naive_mul(&c_b.to_r(), &sk.0.to_r())) - // + Rq::::from_vec_i64(arith::ring::naive_mul( + // + Rq::::from_vec_i64(arith::ring_n::naive_mul(&c_b.to_r(), &sk.0.to_r())) + // + Rq::::from_vec_i64(arith::ring_n::naive_mul( // &c_c.to_r(), - // &R::::from_vec(arith::ring::naive_mul(&sk.0.to_r(), &sk.0.to_r())), + // &R::::from_vec(arith::ring_n::naive_mul(&sk.0.to_r(), &sk.0.to_r())), // )); let m3: Rq = m3.mul_div_round(T, Q); // descale let m3 = m3.remodule::(); diff --git a/ckks/src/encoder.rs b/ckks/src/encoder.rs index 08b8df3..a33378c 100644 --- a/ckks/src/encoder.rs +++ b/ckks/src/encoder.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use arith::{Matrix, Rq, C, R}; +use arith::{Matrix, Ring, Rq, C, R}; #[derive(Clone, Debug)] pub struct SecretKey(Rq); diff --git a/ckks/src/lib.rs b/ckks/src/lib.rs index ba24260..1552d03 100644 --- a/ckks/src/lib.rs +++ b/ckks/src/lib.rs @@ -157,7 +157,7 @@ mod tests { fn test_encode_encrypt_decrypt_decode() -> Result<()> { const Q: u64 = 2u64.pow(16) + 1; const N: usize = 16; - const T: u64 = 16; + const T: u64 = 8; let scale_factor = C::::new(512.0, 0.0); // delta let mut rng = rand::thread_rng(); @@ -202,7 +202,7 @@ mod tests { fn test_add() -> Result<()> { const Q: u64 = 2u64.pow(16) + 1; const N: usize = 16; - const T: u64 = 10; + const T: u64 = 8; let scale_factor = C::::new(1024.0, 0.0); // delta let mut rng = rand::thread_rng(); @@ -245,7 +245,7 @@ mod tests { fn test_sub() -> Result<()> { const Q: u64 = 2u64.pow(16) + 1; const N: usize = 16; - const T: u64 = 10; + const T: u64 = 8; let scale_factor = C::::new(1024.0, 0.0); // delta let mut rng = rand::thread_rng();