use polynomial_ring::Polynomial;
use ring_lwe::utils::{polyadd,polysub};
use crate::utils::{Parameters, add_vec, mul_mat_vec_simple, transpose, mul_vec_simple, gen_small_vector, compress, decompress, encode_message};
pub fn encrypt(
a: &Vec<Vec<Polynomial<i64>>>,
t: &Vec<Polynomial<i64>>,
m_b: &Vec<i64>,
params: &Parameters,
seed: Option<u64>
) -> (Vec<Polynomial<i64>>, Polynomial<i64>) {
let (n, q, k, f, omega) = (params.n, params.q, params.k, ¶ms.f, params.omega);
let r = gen_small_vector(n, k, seed);
let e1 = gen_small_vector(n, k, seed);
let e2 = gen_small_vector(n, 1, seed)[0].clone();
let m = encode_message(&m_b, q);
let u = add_vec(&mul_mat_vec_simple(&transpose(a), &r, q, f, omega), &e1, q, f);
let v = polysub(&polyadd(&mul_vec_simple(t, &r, q, &f, omega), &e2, q, f), &m, q, f);
(u, v)
}
pub fn encrypt_string(pk_string: &String, message_string: &String, params: &Parameters, seed: Option<u64>) -> String {
let (n, k) = (params.n, params.k);
let pk_list: Vec<i64> = decompress(pk_string);
let a: Vec<Vec<Polynomial<i64>>> = pk_list[..k * k * n]
.chunks(k * n)
.map(|chunk| {
chunk.chunks(n).map(|coeffs| Polynomial::new(coeffs.to_vec())).collect()
})
.collect();
let t: Vec<Polynomial<i64>> = pk_list[k * k * n..]
.chunks(n)
.map(|coeffs| Polynomial::new(coeffs.to_vec()))
.collect();
let message_binary: Vec<i64> = message_string
.bytes()
.flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1) as i64))
.collect();
let message_blocks: Vec<Vec<i64>> = message_binary
.chunks(n) .map(|chunk| chunk.to_vec()) .collect();
let mut ciphertext_list = vec![];
for block in message_blocks {
let (u, v) = encrypt(&a, &t, &block, params, seed);
let u_flattened: Vec<i64> = u.iter()
.flat_map(|poly| {
let mut coeffs = poly.coeffs().to_vec();
coeffs.resize(n, 0); coeffs
})
.collect();
let mut v_flattened: Vec<i64> = v.coeffs().to_vec();
v_flattened.resize(n, 0);
ciphertext_list.extend(u_flattened);
ciphertext_list.extend(v_flattened);
}
compress(&ciphertext_list)
}