ring_lwe/
lib.rs

1use polynomial_ring::Polynomial;
2use rand_distr::{Uniform, Normal, Distribution};
3use ntt::{omega,polymul_ntt};
4use rand::SeedableRng;
5use rand::rngs::StdRng;
6
7#[derive(Debug)]
8pub struct Parameters {
9    pub n: usize,       // Polynomial modulus degree
10    pub q: i64,       // Ciphertext modulus
11    pub t: i64,       // Plaintext modulus
12    pub omega: i64,   // n-th root of unity
13    pub f: Polynomial<i64>, // Polynomial modulus (x^n + 1 representation)
14    pub sigma: f64,    // Standard deviation for normal distribution
15}
16
17impl Default for Parameters {
18    fn default() -> Self {
19        let n = 512;
20        let q = 12289;
21        let t = 2;
22        let omega = omega(q, 2*n);
23        let mut poly_vec = vec![0i64;n+1];
24        poly_vec[0] = 1;
25        poly_vec[n] = 1;
26        let f = Polynomial::new(poly_vec);
27        let sigma = 8.0;
28        Parameters {n, q, t, omega, f, sigma}
29    }
30}
31
32pub fn mod_coeffs(x : Polynomial<i64>, modulus : i64) -> Polynomial<i64> {
33	//Take remainder of the coefficients of a polynom by a given modulus
34	//Args:
35	//	x: polynom
36	//	modulus: coefficient modulus
37	//Returns:
38	//	polynomial in Z_modulus[X]
39	let coeffs = x.coeffs();
40	let mut newcoeffs = vec![];
41	let mut c;
42	if coeffs.len() == 0 {
43		// return original input for the zero polynomial
44		x
45	} else {
46		for i in 0..coeffs.len() {
47			c = coeffs[i].rem_euclid(modulus);
48			if c > modulus/2 {
49				c = c-modulus;
50			}
51			newcoeffs.push(c);
52		}
53		Polynomial::new(newcoeffs)
54	}
55}
56
57pub fn polyrem(x: Polynomial<i64>, f: &Polynomial<i64>) -> Polynomial<i64> {
58	//Returns remainder of x modulo f assuming f=x^n+1	
59	let n = f.coeffs().len()-1;
60	let mut coeffs = x.coeffs().to_vec();
61	if coeffs.len() < n+1 {
62		return Polynomial::new(coeffs)
63	} else{
64		for i in n..coeffs.len() {
65			coeffs[i % n] = coeffs[i % n]+(-1 as i64).pow((i/n).try_into().unwrap())*coeffs[i];
66		}
67		coeffs.resize(n,0);
68		Polynomial::new(coeffs)
69	}
70}
71
72pub fn polymul(x : &Polynomial<i64>, y : &Polynomial<i64>, q : i64, f : &Polynomial<i64>) -> Polynomial<i64> {
73    //Multiply two polynoms
74    //Args:
75    //	x, y: two polynoms to be multiplied.
76    //	modulus: coefficient modulus.
77    //	f: polynomial modulus.
78    //Returns:
79    //	polynomial in Z_q[X]/(f).
80	let mut r = x*y;
81    r = polyrem(r,f);
82    if q != 0 {
83        mod_coeffs(r, q)
84    }
85    else{
86        r
87    }
88}
89
90pub fn polymul_fast(
91    x: &Polynomial<i64>, 
92    y: &Polynomial<i64>, 
93    q: i64, 
94    f: &Polynomial<i64>, 
95    omega: i64
96) -> Polynomial<i64> {
97    // Compute the degree and padded coefficients
98    let n = 2 * (x.deg().unwrap() + 1);
99    let x_pad = {
100        let mut coeffs = x.coeffs().to_vec();
101        coeffs.resize(n, 0);
102        coeffs
103    };
104    let y_pad = {
105        let mut coeffs = y.coeffs().to_vec();
106        coeffs.resize(n, 0);
107        coeffs
108    };
109
110    // Perform the polynomial multiplication
111    let r_coeffs = polymul_ntt(&x_pad, &y_pad, n, q, omega);
112
113    // Construct the result polynomial and reduce modulo f
114    let mut r = Polynomial::new(r_coeffs);
115    r = polyrem(r,f);
116    mod_coeffs(r, q)
117}
118
119
120
121pub fn polyadd(x : &Polynomial<i64>, y : &Polynomial<i64>, modulus : i64, f : &Polynomial<i64>) -> Polynomial<i64> {
122    //Add two polynoms
123    //Args:
124    //	x, y: two polynoms to be added.
125    //	modulus: coefficient modulus.
126    //	f: polynomial modulus.
127    //Returns:
128    //	polynomial in Z_modulus[X]/(f).
129	let mut r = x+y;
130    r = polyrem(r,f);
131    if modulus != 0 {
132        mod_coeffs(r, modulus)
133    }
134    else{
135        r
136    }
137}
138
139pub fn polyinv(x : &Polynomial<i64>, modulus: i64) -> Polynomial<i64> {
140    //Additive inverse of polynomial x modulo modulus
141    let y = -x;
142    if modulus != 0{
143      mod_coeffs(y, modulus)
144    }
145    else {
146      y
147    }
148  }
149
150pub fn polysub(x : &Polynomial<i64>, y : &Polynomial<i64>, modulus : i64, f : Polynomial<i64>) -> Polynomial<i64> {
151    //Subtract two polynoms
152    //Args:
153    //	x, y: two polynoms to be added.
154    //	modulus: coefficient modulus.
155    //	f: polynomial modulus.
156    //Returns:
157    //	polynomial in Z_modulus[X]/(f).
158	polyadd(x, &polyinv(y, modulus), modulus, &f)
159}
160
161pub fn gen_binary_poly(size : usize, seed: Option<u64>) -> Polynomial<i64> {
162    //Generates a polynomial with coeffecients in [0, 1]
163    //Args:
164    //	size: number of coeffcients
165    //Returns:
166    //	polynomial of degree size-1
167	let between = Uniform::new(0,2);
168    let mut rng = match seed {
169        Some(seed) => StdRng::seed_from_u64(seed),
170        None => StdRng::from_entropy(),
171    };
172    let mut coeffs = vec![0i64;size];
173	for i in 0..size {
174		coeffs[i] = between.sample(&mut rng);
175	}
176	Polynomial::new(coeffs)
177}
178
179pub fn gen_ternary_poly(size : usize, seed: Option<u64>) -> Polynomial<i64> {
180    //Generates a polynomial with coeffecients in [0, 1]
181    //Args:
182    //	size: number of coeffcients
183    //Returns:
184    //	polynomial of degree size-1 with coeffs in {-1,0,+1}
185	let between = Uniform::new(-1,2);
186    let mut rng = match seed {
187        Some(seed) => StdRng::seed_from_u64(seed),
188        None => StdRng::from_entropy(),
189    };
190    let mut coeffs = vec![0i64;size];
191	for i in 0..size {
192		coeffs[i] = between.sample(&mut rng);
193	}
194	Polynomial::new(coeffs)
195}
196
197pub fn gen_uniform_poly(size: usize, q: i64, seed: Option<u64>) -> Polynomial<i64> {
198    //Generates a polynomial with coeffecients being integers in Z_modulus
199    //Args:
200    //	size: number of coeffcients
201    //Returns:
202    //	polynomial of degree size-1
203	let between = Uniform::new(0,q);
204    let mut rng = match seed {
205        Some(seed) => StdRng::seed_from_u64(seed),
206        None => StdRng::from_entropy(),
207    };
208    let mut coeffs = vec![0i64;size];
209	for i in 0..size {
210		coeffs[i] = between.sample(&mut rng);
211	}
212	mod_coeffs(Polynomial::new(coeffs),q)
213}
214
215pub fn gen_normal_poly(size: usize, sigma: f64, seed: Option<u64>) -> Polynomial<i64> {
216    //Generates a polynomial with coeffecients in a normal distribution
217    //of mean 0 and a standard deviation of 2, then discretize it.
218    //Args:
219    //	size: number of coeffcients,
220    //Returns:
221    //	polynomial of degree size-1
222	let normal = Normal::new(0.0 as f64, sigma).unwrap();
223    let mut rng = match seed {
224        Some(seed) => StdRng::seed_from_u64(seed),
225        None => StdRng::from_entropy(),
226    };
227    let mut coeffs = vec![0i64;size];
228	for i in 0..size {
229		coeffs[i] = normal.sample(&mut rng).round() as i64;
230	}
231	Polynomial::new(coeffs)
232}
233
234//returns the nearest integer to a/b
235pub fn nearest_int(a: i64, b: i64) -> i64 {
236    if a > 0 {
237		(a + b / 2) / b
238	}else {
239		-((-a + b / 2) / b)
240	}
241}