arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
mv arithmetic arith
arnaucube git@arnaucube.com
Sun, 22 Jun 2025 19:15:14 +0200
commit

19457c98ddccc09e3e855a8b4b3ea20871b5a725

parent

7740a3ef3eba21d2455efbc7d31f85b22950faa3

M Cargo.toml → Cargo.toml

@@ -1,7 +1,7 @@

[workspace] members = [ "bfv", - "arithmetic" + "arith" ] resolver = "2"
M arithmetic/Cargo.toml → arith/Cargo.toml

@@ -1,5 +1,5 @@

[package] -name = "arithmetic" +name = "arith" version = "0.1.0" edition = "2024"
M arithmetic/src/naive_ntt.rs → arith/src/naive_ntt.rs

@@ -2,7 +2,7 @@ //! this file implements the non-efficient NTT, which uses multiplication by the

//! Vandermonde matrix. use crate::zq::Zq; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; #[derive(Debug)] pub struct NTT<const Q: u64, const N: usize> {

@@ -101,8 +101,8 @@ mod tests {

use super::*; use rand_distr::Uniform; - use crate::ring::matrix_vec_product; use crate::ring::Rq; + use crate::ring::matrix_vec_product; #[test] fn roots_of_unity() -> Result<()> {
M arithmetic/src/ring.rs → arith/src/ring.rs

@@ -1,8 +1,8 @@

//! Polynomial ring Z[X]/(X^N+1) //! -use anyhow::{anyhow, Result}; -use rand::{distributions::Distribution, Rng}; +use anyhow::{Result, anyhow}; +use rand::{Rng, distributions::Distribution}; use std::array; use std::fmt; use std::ops;
M arithmetic/src/ringq.rs → arith/src/ringq.rs

@@ -1,14 +1,14 @@

//! Polynomial ring Z_q[X]/(X^N+1) //! -use rand::{distributions::Distribution, Rng}; +use rand::{Rng, distributions::Distribution}; use std::array; use std::fmt; use std::ops; use crate::ntt::NTT; -use crate::zq::{modulus_u64, Zq}; -use anyhow::{anyhow, Result}; +use crate::zq::{Zq, modulus_u64}; +use anyhow::{Result, anyhow}; /// PolynomialRing element, where the PolynomialRing is R = Z_q[X]/(X^n +1) /// The implementation assumes that q is prime.
M arithmetic/src/zq.rs → arith/src/zq.rs

@@ -44,11 +44,7 @@ // }

// Zq(e as u64) } pub fn from_bool(b: bool) -> Self { - if b { - Zq(1) - } else { - Zq(0) - } + if b { Zq(1) } else { Zq(0) } } pub fn zero() -> Self { Zq(0u64)
M bfv/Cargo.toml → bfv/Cargo.toml

@@ -9,4 +9,4 @@ rand = { workspace = true }

rand_distr = { workspace = true } itertools = { workspace = true } -arithmetic = { path="../arithmetic" } +arith = { path="../arith" }
M bfv/src/lib.rs → bfv/src/lib.rs

@@ -5,12 +5,12 @@ #![allow(non_camel_case_types)]

#![allow(clippy::upper_case_acronyms)] #![allow(dead_code)] // TMP -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use rand::Rng; use rand_distr::{Normal, Uniform}; use std::ops; -use arithmetic::{Rq, R}; +use arith::{R, Rq}; // error deviation for the Gaussian(Normal) distribution // sigma=3.2 from: https://eprint.iacr.org/2022/162.pdf page 5

@@ -57,7 +57,7 @@ let b0: R<N> = b.0.to_r();

let b1: R<N> = b.1.to_r(); // tensor (\in R) (2021-204 p.9) - use arithmetic::ring::naive_mul; + use arith::ring::naive_mul; // (here can use *, but want to make it explicit that we're using the naive mul) let c0: Vec<i64> = naive_mul(&a0, &b0); let c1_l: Vec<i64> = naive_mul(&a0, &b1);

@@ -66,9 +66,9 @@ let c1: Vec<i64> = itertools::zip_eq(c1_l, c1_r).map(|(l, r)| l + r).collect();

let c2: Vec<i64> = naive_mul(&a1, &b1); // scale down, then reduce module Q, so result is \in R_q - let c0: Rq<Q, N> = arithmetic::ring::mul_div_round::<Q, N>(c0, T, Q); - let c1: Rq<Q, N> = arithmetic::ring::mul_div_round::<Q, N>(c1, T, Q); - let c2: Rq<Q, N> = arithmetic::ring::mul_div_round::<Q, N>(c2, T, Q); + let c0: Rq<Q, N> = arith::ring::mul_div_round::<Q, N>(c0, T, Q); + let c1: Rq<Q, N> = arith::ring::mul_div_round::<Q, N>(c1, T, Q); + let c2: Rq<Q, N> = arith::ring::mul_div_round::<Q, N>(c2, T, Q); (c0, c1, c2) }

@@ -106,7 +106,7 @@ }

} // naive mul in the ring Rq, reusing the ring::naive_mul and then applying mod(X^N +1) fn tmp_naive_mul<const Q: u64, const N: usize>(a: Rq<Q, N>, b: Rq<Q, N>) -> Rq<Q, N> { - Rq::<Q, N>::from_vec_i64(arithmetic::ring::naive_mul(&a.to_r(), &b.to_r())) + Rq::<Q, N>::from_vec_i64(arith::ring::naive_mul(&a.to_r(), &b.to_r())) } impl<const Q: u64, const N: usize> ops::Add<RLWE<Q, N>> for RLWE<Q, N> {

@@ -196,7 +196,7 @@ pub fn decrypt(sk: &SecretKey<Q, N>, c: &RLWE<Q, N>) -> Rq<T, N> {

let cs = c.0 + c.1 * sk.0; // done in mod q // let c1s = tmp_naive_mul(c.1, sk.0); - // // let c1s = arithmetic::ring::naive_mul(&c.1.to_r(), &sk.0.to_r()); // TODO rm + // // let c1s = arith::ring::naive_mul(&c.1.to_r(), &sk.0.to_r()); // TODO rm // // let c1s = Rq::<Q, N>::from_vec_i64(c1s); // let cs = c.0 + c1s;

@@ -293,12 +293,12 @@ // let c2 = c2.remodule::<PQ>();

// let c2 = c2.to_r(); // let c2rlk0: Vec<f64> = (c2.remodule::<PQ>() * rlk.0) - use arithmetic::ring::naive_mul; + use arith::ring::naive_mul; let c2rlk0: Vec<i64> = naive_mul(&c2.to_r(), &rlk.0.to_r()); let c2rlk1: Vec<i64> = naive_mul(&c2.to_r(), &rlk.1.to_r()); - let r0: Rq<Q, N> = arithmetic::ring::mul_div_round::<Q, N>(c2rlk0, 1, P); - let r1: Rq<Q, N> = arithmetic::ring::mul_div_round::<Q, N>(c2rlk1, 1, P); + let r0: Rq<Q, N> = arith::ring::mul_div_round::<Q, N>(c2rlk0, 1, P); + let r1: Rq<Q, N> = arith::ring::mul_div_round::<Q, N>(c2rlk1, 1, P); // let r0 = Rq::<Q, N>::from_vec_f64(c2rlk0); // let r1 = Rq::<Q, N>::from_vec_f64(c2rlk1);

@@ -409,7 +409,7 @@ #[test]

fn test_tensor() -> Result<()> { const Q: u64 = 2u64.pow(16) + 1; // q prime, and 2^q + 1 shape const N: usize = 32; - const T: u64 = 8; // plaintext modulus + const T: u64 = 2; // plaintext modulus // const P: u64 = Q; const P: u64 = Q * Q;

@@ -442,10 +442,10 @@

// decrypt non-relinearized mul result let m3: Rq<Q, N> = c_a + c_b * sk.0 + c_c * sk.0 * sk.0; // let m3: Rq<Q, N> = c_a - // + Rq::<Q, N>::from_vec_i64(arithmetic::ring::naive_mul(&c_b.to_r(), &sk.0.to_r())) - // + Rq::<Q, N>::from_vec_i64(arithmetic::ring::naive_mul( + // + Rq::<Q, N>::from_vec_i64(arith::ring::naive_mul(&c_b.to_r(), &sk.0.to_r())) + // + Rq::<Q, N>::from_vec_i64(arith::ring::naive_mul( // &c_c.to_r(), - // &R::<N>::from_vec(arithmetic::ring::naive_mul(&sk.0.to_r(), &sk.0.to_r())), + // &R::<N>::from_vec(arith::ring::naive_mul(&sk.0.to_r(), &sk.0.to_r())), // )); let m3: Rq<Q, N> = m3.mul_div_round(T, Q); // descale let m3 = m3.remodule::<T>();

@@ -543,11 +543,13 @@

let m3 = BFV::<Q, N, T>::decrypt(&sk, &c3); let naive = (m1.to_r() * m2.to_r()).to_rq::<T>(); - assert_eq!(m3.coeffs().to_vec(), naive.coeffs().to_vec(), + assert_eq!( + m3.coeffs().to_vec(), + naive.coeffs().to_vec(), "\n\nfor testing:\nlet m1 = Rq::<T, N>::from_vec_u64(vec!{:?});\nlet m2 = Rq::<T, N>::from_vec_u64(vec!{:?});\n", m1.coeffs(), m2.coeffs() - ); + ); Ok(()) }