use std::ffi::c_void;
use std::fmt::Debug;
use std::ptr::null_mut;
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering;
use crate::bindgen;
use crate::error::*;
use crate::try_seal;
use crate::{Context, Plaintext};
pub struct BFVEncoder {
handle: AtomicPtr<c_void>,
}
impl BFVEncoder {
pub fn new(ctx: &Context) -> Result<Self> {
let mut handle: *mut c_void = null_mut();
try_seal!(unsafe { bindgen::BatchEncoder_Create(ctx.get_handle(), &mut handle) })?;
Ok(Self {
handle: AtomicPtr::new(handle),
})
}
pub(crate) unsafe fn get_handle(&self) -> *mut c_void {
self.handle.load(Ordering::SeqCst)
}
pub fn get_slot_count(&self) -> usize {
let mut count: u64 = 0;
try_seal!(unsafe { bindgen::BatchEncoder_GetSlotCount(self.get_handle(), &mut count) })
.expect("Internal error in BVTEncoder::get_slot_count().");
count as usize
}
pub fn encode_u64(
&self,
data: &[u64],
) -> Result<Plaintext> {
let plaintext = Plaintext::new()?;
try_seal!(unsafe {
bindgen::BatchEncoder_Encode1(
self.get_handle(),
data.len() as u64,
data.as_ptr() as *mut u64,
plaintext.get_handle(),
)
})?;
Ok(plaintext)
}
pub fn decode_u64(
&self,
plaintext: &Plaintext,
) -> Result<Vec<u64>> {
let mut data = Vec::with_capacity(self.get_slot_count());
let data_ptr = data.as_mut_ptr();
let mut size: u64 = 0;
try_seal!(unsafe {
bindgen::BatchEncoder_Decode1(
self.get_handle(),
plaintext.get_handle(),
&mut size,
data_ptr,
null_mut(),
)
})?;
if data.capacity() < size as usize {
panic!("Allocation overflow BVTEncoder::decode_unsigned");
}
unsafe {
data.set_len(size as usize);
}
Ok(data)
}
pub fn encode_i64(
&self,
data: &[i64],
) -> Result<Plaintext> {
let plaintext = Plaintext::new()?;
try_seal!(unsafe {
bindgen::BatchEncoder_Encode2(
self.get_handle(),
data.len() as u64,
data.as_ptr() as *mut i64,
plaintext.get_handle(),
)
})?;
Ok(plaintext)
}
pub fn decode_i64(
&self,
plaintext: &Plaintext,
) -> Result<Vec<i64>> {
let mut data = Vec::with_capacity(self.get_slot_count());
let data_ptr = data.as_mut_ptr();
let mut size: u64 = 0;
try_seal!(unsafe {
bindgen::BatchEncoder_Decode2(
self.get_handle(),
plaintext.get_handle(),
&mut size,
data_ptr,
null_mut(),
)
})?;
if data.capacity() < size as usize {
panic!("Allocation overflow BVTEncoder::decode_unsigned");
}
unsafe {
data.set_len(size as usize);
}
Ok(data)
}
pub fn encode_f64(
&self,
data: &[f64],
base: f64,
) -> Result<Plaintext> {
let unsigned_data: Vec<u64> = data.iter().map(|v| (v * base).round() as u64).collect();
self.encode_u64(&unsigned_data)
}
pub fn decode_f64(
&self,
plaintext: &Plaintext,
base: f64,
) -> Result<Vec<f64>> {
let unsigned_data: Vec<u64> = self.decode_u64(plaintext)?;
Ok(unsigned_data.iter().map(|v| *v as f64 / base).collect())
}
}
impl Drop for BFVEncoder {
fn drop(&mut self) {
try_seal!(unsafe { bindgen::BatchEncoder_Destroy(self.get_handle()) })
.expect("Internal error in BFVEncoder::drop.");
}
}
impl Debug for BFVEncoder {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
f.debug_struct("BFVEncoder")
.field("handle", &self.handle)
.finish()
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn can_create_and_drop_bfv_encoder() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
std::mem::drop(encoder);
}
#[test]
fn can_get_slots_bfv_encoder() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
assert_eq!(encoder.get_slot_count(), 8192);
}
#[test]
fn can_get_encode_and_decode_unsigned() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
let mut data = Vec::with_capacity(8192);
for i in 0..encoder.get_slot_count() {
data.push(i as u64);
}
let plaintext = encoder.encode_u64(&data).unwrap();
let data_2 = encoder.decode_u64(&plaintext).unwrap();
assert_eq!(data, data_2);
}
#[test]
fn can_get_encode_and_decode_signed() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
let mut data = Vec::with_capacity(8192);
for i in 0..encoder.get_slot_count() {
data.push(i as i64);
}
let plaintext = encoder.encode_i64(&data).unwrap();
let data_2 = encoder.decode_i64(&plaintext).unwrap();
assert_eq!(data, data_2);
}
#[test]
fn scalar_encoder_can_encode_decode_signed() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
let encoded = encoder.encode_i64(&[-15i64]).unwrap();
let decoded = encoder.decode_i64(&encoded).unwrap();
assert_eq!(decoded[0], -15);
}
#[test]
fn scalar_encoder_can_encode_decode_unsigned() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let encoder = BFVEncoder::new(&ctx).unwrap();
let encoded = encoder.encode_i64(&[42i64]).unwrap();
let decoded = encoder.decode_i64(&encoded).unwrap();
assert_eq!(decoded[0], 42);
}
#[test]
#[ignore = "Not working yet because of integer size limitation of BFV"]
fn can_get_encode_and_decode_float() {
let params = BFVEncryptionParametersBuilder::new()
.set_poly_modulus_degree(DegreeType::D8192)
.set_coefficient_modulus(
CoefficientModulusFactory::build(DegreeType::D8192, &[50, 30, 30, 50, 50]).unwrap(),
)
.set_plain_modulus(PlainModulusFactory::batching(DegreeType::D8192, 20).unwrap())
.build()
.unwrap();
let ctx = Context::new(¶ms, false, SecurityLevel::TC128).unwrap();
let base = 2.0f64.powi(40);
let encoder = BFVEncoder::new(&ctx).unwrap();
let encoded = encoder.encode_f64(&[42f64], base).unwrap();
let decoded = encoder.decode_f64(&encoded, base).unwrap();
assert!((decoded[0] - 42f64).abs() < 1e-10);
}
}