mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
12 Commits
bb/fix/sum
...
clean_smar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce625df1f6 | ||
|
|
5f16704fa8 | ||
|
|
d686cd86b5 | ||
|
|
a2a3f10341 | ||
|
|
d7ac3b6d15 | ||
|
|
44b9241c83 | ||
|
|
9df1725efc | ||
|
|
55d97ed794 | ||
|
|
d10c11720e | ||
|
|
f1e0742b9f | ||
|
|
e6136ca418 | ||
|
|
0805cd69e0 |
@@ -149,7 +149,7 @@ impl KreyviumStreamShortint {
|
||||
.unchecked_add_assign(&mut new_c, c5);
|
||||
self.internal_server_key
|
||||
.unchecked_add_assign(&mut new_c, &temp_b);
|
||||
self.internal_server_key.clear_carry_assign(&mut new_c);
|
||||
self.internal_server_key.message_extract_assign(&mut new_c);
|
||||
new_c
|
||||
},
|
||||
|| {
|
||||
|
||||
@@ -113,7 +113,7 @@ impl TriviumStreamShortint {
|
||||
.unchecked_add_assign(&mut new_a, a5);
|
||||
self.internal_server_key
|
||||
.unchecked_add_assign(&mut new_a, &temp_c);
|
||||
self.internal_server_key.clear_carry_assign(&mut new_a);
|
||||
self.internal_server_key.message_extract_assign(&mut new_a);
|
||||
new_a
|
||||
},
|
||||
|| {
|
||||
@@ -122,7 +122,7 @@ impl TriviumStreamShortint {
|
||||
.unchecked_add_assign(&mut new_b, b5);
|
||||
self.internal_server_key
|
||||
.unchecked_add_assign(&mut new_b, &temp_a);
|
||||
self.internal_server_key.clear_carry_assign(&mut new_b);
|
||||
self.internal_server_key.message_extract_assign(&mut new_b);
|
||||
new_b
|
||||
},
|
||||
)
|
||||
@@ -135,7 +135,7 @@ impl TriviumStreamShortint {
|
||||
.unchecked_add_assign(&mut new_c, c5);
|
||||
self.internal_server_key
|
||||
.unchecked_add_assign(&mut new_c, &temp_b);
|
||||
self.internal_server_key.clear_carry_assign(&mut new_c);
|
||||
self.internal_server_key.message_extract_assign(&mut new_c);
|
||||
new_c
|
||||
},
|
||||
|| {
|
||||
|
||||
@@ -367,14 +367,14 @@ fn main() {
|
||||
let modulus = client_key.parameters.message_modulus().0 as u64;
|
||||
|
||||
// We use the private client key to encrypt two messages:
|
||||
let ct_1 = client_key.encrypt(msg1);
|
||||
let mut ct_1 = client_key.encrypt(msg1);
|
||||
let mut ct_2 = client_key.encrypt(msg2);
|
||||
|
||||
// Compute the lookup table for the bivariate functions
|
||||
let acc = server_key.generate_lookup_table_bivariate(|x,y| (x.count_ones()
|
||||
+ y.count_ones()) as u64 % modulus );
|
||||
|
||||
let ct_res = server_key.smart_apply_lookup_table_bivariate(&ct_1, &mut ct_2, &acc);
|
||||
let ct_res = server_key.smart_apply_lookup_table_bivariate(&mut ct_1, &mut ct_2, &acc);
|
||||
|
||||
// We use the client key to decrypt the output of the circuit:
|
||||
let output = client_key.decrypt(&ct_res);
|
||||
|
||||
@@ -280,7 +280,7 @@ pub unsafe extern "C" fn shortint_server_key_unchecked_not_equal(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -288,9 +288,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key.0.smart_scalar_greater(&ct_left.0, right);
|
||||
let res = server_key.0.smart_scalar_greater(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
@@ -301,7 +301,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -309,11 +309,11 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key
|
||||
.0
|
||||
.smart_scalar_greater_or_equal(&ct_left.0, right);
|
||||
.smart_scalar_greater_or_equal(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
@@ -324,7 +324,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_greater_or_equal(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -332,9 +332,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key.0.smart_scalar_less(&ct_left.0, right);
|
||||
let res = server_key.0.smart_scalar_less(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
@@ -345,7 +345,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -353,9 +353,11 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key.0.smart_scalar_less_or_equal(&ct_left.0, right);
|
||||
let res = server_key
|
||||
.0
|
||||
.smart_scalar_less_or_equal(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
@@ -366,7 +368,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_less_or_equal(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -374,9 +376,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key.0.smart_scalar_equal(&ct_left.0, right);
|
||||
let res = server_key.0.smart_scalar_equal(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
@@ -387,7 +389,7 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_equal(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shortint_server_key_smart_scalar_not_equal(
|
||||
server_key: *const ShortintServerKey,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
right: u8,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -395,9 +397,9 @@ pub unsafe extern "C" fn shortint_server_key_smart_scalar_not_equal(
|
||||
check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
|
||||
let res = server_key.0.smart_scalar_not_equal(&ct_left.0, right);
|
||||
let res = server_key.0.smart_scalar_not_equal(&mut ct_left.0, right);
|
||||
|
||||
let heap_allocated_ct_result = Box::new(ShortintCiphertext(res));
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ pub unsafe extern "C" fn shortint_server_key_generate_bivariate_pbs_lookup_table
|
||||
pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
|
||||
server_key: *const ShortintServerKey,
|
||||
lookup_table: *const ShortintBivariatePBSLookupTable,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
ct_right: *mut ShortintCiphertext,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -132,14 +132,14 @@ pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let lookup_table = get_ref_checked(lookup_table).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
let ct_right = get_mut_checked(ct_right).unwrap();
|
||||
|
||||
let res = crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine
|
||||
.smart_apply_lookup_table_bivariate(
|
||||
&server_key.0,
|
||||
&ct_left.0,
|
||||
&mut ct_left.0,
|
||||
&mut ct_right.0,
|
||||
&lookup_table.0,
|
||||
)
|
||||
|
||||
@@ -298,7 +298,9 @@ where
|
||||
}
|
||||
|
||||
pub(crate) fn scalar_equal(&self, lhs: &GenericShortInt<P>, scalar: u8) -> GenericShortInt<P> {
|
||||
let ciphertext = self.key.scalar_equal(&lhs.ciphertext.borrow(), scalar);
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_equal(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
@@ -310,7 +312,9 @@ where
|
||||
lhs: &GenericShortInt<P>,
|
||||
scalar: u8,
|
||||
) -> GenericShortInt<P> {
|
||||
let ciphertext = self.key.scalar_not_equal(&lhs.ciphertext.borrow(), scalar);
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_not_equal(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
@@ -324,7 +328,7 @@ where
|
||||
) -> GenericShortInt<P> {
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_greater_or_equal(&lhs.ciphertext.borrow(), scalar);
|
||||
.scalar_greater_or_equal(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
@@ -338,7 +342,7 @@ where
|
||||
) -> GenericShortInt<P> {
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_less_or_equal(&lhs.ciphertext.borrow(), scalar);
|
||||
.scalar_less_or_equal(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
@@ -350,7 +354,9 @@ where
|
||||
lhs: &GenericShortInt<P>,
|
||||
scalar: u8,
|
||||
) -> GenericShortInt<P> {
|
||||
let ciphertext = self.key.scalar_greater(&lhs.ciphertext.borrow(), scalar);
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_greater(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
@@ -358,7 +364,9 @@ where
|
||||
}
|
||||
|
||||
pub(crate) fn scalar_less(&self, lhs: &GenericShortInt<P>, scalar: u8) -> GenericShortInt<P> {
|
||||
let ciphertext = self.key.scalar_less(&lhs.ciphertext.borrow(), scalar);
|
||||
let ciphertext = self
|
||||
.key
|
||||
.scalar_less(&mut lhs.ciphertext.borrow_mut(), scalar);
|
||||
GenericShortInt {
|
||||
ciphertext: RefCell::new(ciphertext),
|
||||
id: lhs.id,
|
||||
|
||||
@@ -34,6 +34,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign(ct_left);
|
||||
self.full_extract_message_assign(ct_right);
|
||||
}
|
||||
assert!(self.is_crt_add_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_crt_add(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -43,6 +45,7 @@ impl ServerKey {
|
||||
self.full_extract_message_assign(ct_left);
|
||||
self.full_extract_message_assign(ct_right);
|
||||
}
|
||||
assert!(self.is_crt_add_possible(ct_left, ct_right));
|
||||
self.unchecked_crt_add_assign(ct_left, ct_right);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,11 @@ impl ServerKey {
|
||||
}
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub fn smart_crt_mul(
|
||||
&self,
|
||||
ct_left: &CrtCiphertext,
|
||||
ct_left: &mut CrtCiphertext,
|
||||
ct_right: &mut CrtCiphertext,
|
||||
) -> CrtCiphertext {
|
||||
let mut ct_res = ct_left.clone();
|
||||
|
||||
@@ -73,6 +73,8 @@ impl ServerKey {
|
||||
if !self.is_crt_neg_possible(ctxt) {
|
||||
self.full_extract_message_assign(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_neg_possible(ctxt));
|
||||
|
||||
self.unchecked_crt_neg_assign(ctxt);
|
||||
}
|
||||
|
||||
@@ -80,6 +82,7 @@ impl ServerKey {
|
||||
if !self.is_crt_neg_possible(ctxt) {
|
||||
self.full_extract_message_assign(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_neg_possible(ctxt));
|
||||
self.unchecked_crt_neg(ctxt)
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_add_possible(ct, scalar));
|
||||
|
||||
let mut ct = ct.clone();
|
||||
self.unchecked_crt_scalar_add_assign(&mut ct, scalar);
|
||||
ct
|
||||
@@ -207,6 +209,9 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_add_possible(ct, scalar) {
|
||||
self.full_extract_message_assign(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_add_possible(ct, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_add_assign(ct, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,9 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_extract_message_assign(ctxt);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_mul_possible(ctxt, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_mul(ctxt, scalar)
|
||||
}
|
||||
|
||||
@@ -212,6 +215,8 @@ impl ServerKey {
|
||||
if !self.is_crt_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_extract_message_assign(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_scalar_mul_possible(ctxt, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_mul_assign(ctxt, scalar);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_sub_possible(ct, scalar) {
|
||||
self.full_extract_message_assign(ct);
|
||||
}
|
||||
assert!(self.is_crt_scalar_sub_possible(ct, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_sub(ct, scalar)
|
||||
}
|
||||
@@ -198,6 +199,7 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_sub_possible(ct, scalar) {
|
||||
self.full_extract_message_assign(ct);
|
||||
}
|
||||
assert!(self.is_crt_scalar_sub_possible(ct, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_sub_assign(ct, scalar);
|
||||
}
|
||||
|
||||
@@ -111,6 +111,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign(ctxt_right);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
let mut result = ctxt_left.clone();
|
||||
self.unchecked_crt_sub_assign(&mut result, ctxt_right);
|
||||
|
||||
@@ -152,6 +154,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign(ctxt_right);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
self.unchecked_crt_sub_assign(ctxt_left, ctxt_right);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ impl ServerKey {
|
||||
|| self.full_extract_message_assign(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_crt_add_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_crt_add_assign_parallelized(ct_left, ct_right);
|
||||
}
|
||||
|
||||
@@ -110,6 +112,8 @@ impl ServerKey {
|
||||
|| self.full_extract_message_assign(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_crt_add_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_crt_add_parallelized(ct_left, ct_right)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +96,12 @@ impl ServerKey {
|
||||
self.key.smart_mul_lsb_assign(block_left, block_right);
|
||||
});
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub fn smart_crt_mul_parallelized(
|
||||
&self,
|
||||
ct_left: &CrtCiphertext,
|
||||
ct_left: &mut CrtCiphertext,
|
||||
ct_right: &mut CrtCiphertext,
|
||||
) -> CrtCiphertext {
|
||||
let mut ct_res = ct_left.clone();
|
||||
|
||||
@@ -72,6 +72,7 @@ impl ServerKey {
|
||||
if !self.is_crt_neg_possible(ctxt) {
|
||||
self.full_extract_message_assign_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_neg_possible(ctxt));
|
||||
self.unchecked_crt_neg_assign_parallelized(ctxt);
|
||||
}
|
||||
|
||||
@@ -79,6 +80,7 @@ impl ServerKey {
|
||||
if !self.is_crt_neg_possible(ctxt) {
|
||||
self.full_extract_message_assign_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_neg_possible(ctxt));
|
||||
self.unchecked_crt_neg_parallelized(ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign_parallelized(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_add_possible(ct, scalar));
|
||||
|
||||
let mut ct = ct.clone();
|
||||
self.unchecked_crt_scalar_add_assign_parallelized(&mut ct, scalar);
|
||||
ct
|
||||
@@ -189,6 +191,8 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_add_possible(ct, scalar) {
|
||||
self.full_extract_message_assign_parallelized(ct);
|
||||
}
|
||||
assert!(self.is_crt_scalar_add_possible(ct, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_add_assign_parallelized(ct, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ impl ServerKey {
|
||||
if !self.is_crt_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_extract_message_assign_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_scalar_mul_possible(ctxt, scalar));
|
||||
self.unchecked_crt_scalar_mul(ctxt, scalar)
|
||||
}
|
||||
|
||||
@@ -194,6 +195,8 @@ impl ServerKey {
|
||||
if !self.is_crt_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_extract_message_assign_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_crt_scalar_mul_possible(ctxt, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_mul_assign_parallelized(ctxt, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ impl ServerKey {
|
||||
self.full_extract_message_assign_parallelized(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_sub_possible(ct, scalar));
|
||||
self.unchecked_crt_scalar_sub_parallelized(ct, scalar)
|
||||
}
|
||||
|
||||
@@ -181,6 +182,8 @@ impl ServerKey {
|
||||
self.full_extract_message_assign_parallelized(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_scalar_sub_possible(ct, scalar));
|
||||
|
||||
self.unchecked_crt_scalar_sub_assign_parallelized(ct, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,8 @@ impl ServerKey {
|
||||
);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
self.unchecked_crt_sub_parallelized(ctxt_left, ctxt_right)
|
||||
}
|
||||
|
||||
@@ -153,6 +155,8 @@ impl ServerKey {
|
||||
);
|
||||
}
|
||||
|
||||
assert!(self.is_crt_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
self.unchecked_crt_sub_assign_parallelized(ctxt_left, ctxt_right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +237,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
|
||||
assert!(self.is_add_possible(ct_left, ct_right));
|
||||
self.unchecked_add(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -249,6 +251,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
assert!(self.is_add_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_add_assign(ct_left, ct_right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitand(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -225,6 +226,7 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitand_assign(ct_left, ct_right);
|
||||
}
|
||||
|
||||
@@ -400,6 +402,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitor(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -412,6 +416,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitor_assign(ct_left, ct_right);
|
||||
}
|
||||
|
||||
@@ -587,6 +593,7 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitxor(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -599,6 +606,7 @@ impl ServerKey {
|
||||
self.full_propagate(ct_left);
|
||||
self.full_propagate(ct_right);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitxor_assign(ct_left, ct_right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,10 +129,13 @@ impl ServerKey {
|
||||
/// let res: u64 = cks.decrypt(&ct_res);
|
||||
/// assert_eq!((clear_1 * clear_2) % 256, res);
|
||||
/// ```
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub fn smart_block_mul(
|
||||
&self,
|
||||
ct1: &mut RadixCiphertext,
|
||||
ct2: &crate::shortint::Ciphertext,
|
||||
ct2: &mut crate::shortint::Ciphertext,
|
||||
index: usize,
|
||||
) -> RadixCiphertext {
|
||||
//Makes sure we can do the multiplications
|
||||
@@ -160,7 +163,7 @@ impl ServerKey {
|
||||
pub fn smart_block_mul_assign(
|
||||
&self,
|
||||
ct1: &mut RadixCiphertext,
|
||||
ct2: &crate::shortint::Ciphertext,
|
||||
ct2: &mut crate::shortint::Ciphertext,
|
||||
index: usize,
|
||||
) {
|
||||
*ct1 = self.smart_block_mul(ct1, ct2, index);
|
||||
|
||||
@@ -210,6 +210,7 @@ impl ServerKey {
|
||||
if !self.is_neg_possible(ctxt) {
|
||||
self.full_propagate(ctxt);
|
||||
}
|
||||
assert!(self.is_neg_possible(ctxt));
|
||||
self.unchecked_neg(ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +200,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_scalar_add_possible(ct, scalar));
|
||||
|
||||
let mut ct = ct.clone();
|
||||
self.unchecked_scalar_add_assign(&mut ct, scalar);
|
||||
ct
|
||||
@@ -239,6 +241,7 @@ impl ServerKey {
|
||||
if !self.is_scalar_add_possible(ct, scalar) {
|
||||
self.full_propagate(ct);
|
||||
}
|
||||
assert!(self.is_scalar_add_possible(ct, scalar));
|
||||
self.unchecked_scalar_add_assign(ct, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ impl ServerKey {
|
||||
if !self.is_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_propagate(ctxt);
|
||||
}
|
||||
assert!(self.is_small_scalar_mul_possible(ctxt, scalar));
|
||||
self.unchecked_small_scalar_mul(ctxt, scalar)
|
||||
}
|
||||
|
||||
@@ -293,6 +294,8 @@ impl ServerKey {
|
||||
if !self.is_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_propagate(ctxt);
|
||||
}
|
||||
assert!(self.is_small_scalar_mul_possible(ctxt, scalar));
|
||||
|
||||
self.unchecked_small_scalar_mul_assign(ctxt, scalar);
|
||||
}
|
||||
|
||||
|
||||
@@ -288,6 +288,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_scalar_sub_possible(ct, scalar));
|
||||
|
||||
self.unchecked_scalar_sub(ct, scalar)
|
||||
}
|
||||
|
||||
@@ -299,6 +301,8 @@ impl ServerKey {
|
||||
self.full_propagate(ct);
|
||||
}
|
||||
|
||||
assert!(self.is_scalar_sub_possible(ct, scalar));
|
||||
|
||||
self.unchecked_scalar_sub_assign(ct, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +254,8 @@ impl ServerKey {
|
||||
self.full_propagate(ctxt_right);
|
||||
}
|
||||
|
||||
assert!(self.is_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
let mut result = ctxt_left.clone();
|
||||
self.unchecked_sub_assign(&mut result, ctxt_right);
|
||||
|
||||
@@ -302,6 +304,8 @@ impl ServerKey {
|
||||
self.full_propagate(ctxt_right);
|
||||
}
|
||||
|
||||
assert!(self.is_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
self.unchecked_sub_assign(ctxt_left, ctxt_right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,14 +912,14 @@ fn integer_smart_block_mul(param: ClassicPBSParameters) {
|
||||
|
||||
// Encrypt the integers
|
||||
let ctxt_1 = cks.encrypt_radix(clear1, NB_CTXT);
|
||||
let ctxt_2 = cks.encrypt_one_block(clear2);
|
||||
let mut ctxt_2 = cks.encrypt_one_block(clear2);
|
||||
|
||||
let mut res = ctxt_1.clone();
|
||||
let mut clear = clear1;
|
||||
|
||||
res = sks.smart_block_mul(&mut res, &ctxt_2, 0);
|
||||
res = sks.smart_block_mul(&mut res, &mut ctxt_2, 0);
|
||||
for _ in 0..5 {
|
||||
res = sks.smart_block_mul(&mut res, &ctxt_2, 0);
|
||||
res = sks.smart_block_mul(&mut res, &mut ctxt_2, 0);
|
||||
clear = (clear * clear2) % modulus;
|
||||
}
|
||||
let dec: u64 = cks.decrypt_radix(&res);
|
||||
|
||||
@@ -98,6 +98,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
|
||||
assert!(self.is_add_possible(ct_left, ct_right));
|
||||
self.unchecked_add(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -111,6 +113,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
|
||||
assert!(self.is_add_possible(ct_left, ct_right));
|
||||
self.unchecked_add_assign(ct_left, ct_right);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitand_parallelized(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -80,6 +81,7 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitand_assign_parallelized(ct_left, ct_right);
|
||||
}
|
||||
|
||||
@@ -227,6 +229,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_bitor_parallelized(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -241,6 +245,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_bitor_assign_parallelized(ct_left, ct_right);
|
||||
}
|
||||
|
||||
@@ -388,6 +394,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_bitxor_parallelized(ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -402,6 +410,8 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_bitxor_assign_parallelized(ct_left, ct_right);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,6 @@ impl ServerKey {
|
||||
true_ct: &RadixCiphertext,
|
||||
false_ct: &RadixCiphertext,
|
||||
) -> RadixCiphertext {
|
||||
assert!(
|
||||
condition.holds_boolean_value(),
|
||||
"The condition ciphertext does not encrypt a boolean (0 or 1) value"
|
||||
);
|
||||
|
||||
let condition_block = &condition.blocks[0];
|
||||
self.unchecked_programmable_if_then_else_parallelized(
|
||||
condition_block,
|
||||
@@ -202,7 +197,7 @@ impl ServerKey {
|
||||
condition_block: &crate::shortint::Ciphertext,
|
||||
value: u64,
|
||||
) {
|
||||
assert!(condition_block.degree.0 < condition_block.message_modulus.0);
|
||||
// assert!(condition_block.degree.0 < condition_block.message_modulus.0);
|
||||
assert!(value < condition_block.message_modulus.0 as u64);
|
||||
|
||||
self.zero_out_if(ct, condition_block, |x| x == value);
|
||||
@@ -216,7 +211,7 @@ impl ServerKey {
|
||||
) where
|
||||
F: Fn(u64) -> bool,
|
||||
{
|
||||
assert!(condition_block.degree.0 < condition_block.message_modulus.0);
|
||||
// assert!(condition_block.degree.0 < condition_block.message_modulus.0);
|
||||
|
||||
if condition_block.degree.0 == 0 {
|
||||
return self.create_trivial_zero_assign_radix(ct);
|
||||
|
||||
@@ -133,10 +133,13 @@ impl ServerKey {
|
||||
/// let res: u64 = cks.decrypt(&ct_res);
|
||||
/// assert_eq!((clear_1 * clear_2) % 256, res);
|
||||
/// ```
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub fn smart_block_mul_parallelized(
|
||||
&self,
|
||||
ct1: &mut RadixCiphertext,
|
||||
ct2: &crate::shortint::Ciphertext,
|
||||
ct2: &mut crate::shortint::Ciphertext,
|
||||
index: usize,
|
||||
) -> RadixCiphertext {
|
||||
//Makes sure we can do the multiplications
|
||||
@@ -217,7 +220,7 @@ impl ServerKey {
|
||||
(true, true) => (ct1, ct2),
|
||||
(true, false) => {
|
||||
tmp_rhs = ct2.clone();
|
||||
self.key.clear_carry_assign(&mut tmp_rhs);
|
||||
self.key.message_extract_assign(&mut tmp_rhs);
|
||||
(ct1, &tmp_rhs)
|
||||
}
|
||||
(false, true) => {
|
||||
@@ -228,7 +231,7 @@ impl ServerKey {
|
||||
tmp_rhs = ct2.clone();
|
||||
rayon::join(
|
||||
|| self.full_propagate_parallelized(ct1),
|
||||
|| self.key.clear_carry_assign(&mut tmp_rhs),
|
||||
|| self.key.message_extract_assign(&mut tmp_rhs),
|
||||
);
|
||||
(ct1, &tmp_rhs)
|
||||
}
|
||||
@@ -266,7 +269,7 @@ impl ServerKey {
|
||||
pub fn smart_block_mul_assign_parallelized(
|
||||
&self,
|
||||
ct1: &mut RadixCiphertext,
|
||||
ct2: &crate::shortint::Ciphertext,
|
||||
ct2: &mut crate::shortint::Ciphertext,
|
||||
index: usize,
|
||||
) {
|
||||
*ct1 = self.smart_block_mul_parallelized(ct1, ct2, index);
|
||||
|
||||
@@ -32,6 +32,7 @@ impl ServerKey {
|
||||
if !self.is_neg_possible(ctxt) {
|
||||
self.full_propagate_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_neg_possible(ctxt));
|
||||
self.unchecked_neg(ctxt)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ impl ServerKey {
|
||||
if !self.is_scalar_add_possible(ct, scalar) {
|
||||
self.full_propagate_parallelized(ct);
|
||||
}
|
||||
assert!(self.is_scalar_add_possible(ct, scalar));
|
||||
self.unchecked_scalar_add(ct, scalar)
|
||||
}
|
||||
|
||||
@@ -74,6 +75,7 @@ impl ServerKey {
|
||||
if !self.is_scalar_add_possible(ct, scalar) {
|
||||
self.full_propagate_parallelized(ct);
|
||||
}
|
||||
assert!(self.is_scalar_add_possible(ct, scalar));
|
||||
self.unchecked_scalar_add_assign(ct, scalar);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,6 +179,7 @@ impl ServerKey {
|
||||
if !self.is_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_propagate_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_small_scalar_mul_possible(ctxt, scalar));
|
||||
self.unchecked_small_scalar_mul_parallelized(ctxt, scalar)
|
||||
}
|
||||
|
||||
@@ -221,6 +222,7 @@ impl ServerKey {
|
||||
if !self.is_small_scalar_mul_possible(ctxt, scalar) {
|
||||
self.full_propagate_parallelized(ctxt);
|
||||
}
|
||||
assert!(self.is_small_scalar_mul_possible(ctxt, scalar));
|
||||
self.unchecked_small_scalar_mul_assign_parallelized(ctxt, scalar);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ impl ServerKey {
|
||||
if !self.is_scalar_sub_possible(ct, scalar) {
|
||||
self.full_propagate_parallelized(ct);
|
||||
}
|
||||
assert!(self.is_scalar_sub_possible(ct, scalar));
|
||||
self.unchecked_scalar_sub(ct, scalar)
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ impl ServerKey {
|
||||
if !self.is_scalar_sub_possible(ct, scalar) {
|
||||
self.full_propagate_parallelized(ct);
|
||||
}
|
||||
assert!(self.is_scalar_sub_possible(ct, scalar));
|
||||
self.unchecked_scalar_sub_assign(ct, scalar);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ impl ServerKey {
|
||||
);
|
||||
}
|
||||
|
||||
assert!(self.is_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
let mut result = ctxt_left.clone();
|
||||
self.unchecked_sub_assign(&mut result, ctxt_right);
|
||||
|
||||
@@ -95,6 +97,7 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ctxt_right),
|
||||
);
|
||||
}
|
||||
assert!(self.is_sub_possible(ctxt_left, ctxt_right));
|
||||
|
||||
self.unchecked_sub_assign(ctxt_left, ctxt_right);
|
||||
}
|
||||
|
||||
@@ -2435,14 +2435,14 @@ where
|
||||
|
||||
// Encrypt the integers
|
||||
let ctxt_1 = cks.encrypt(clear1);
|
||||
let ctxt_2 = cks.encrypt_one_block(clear2);
|
||||
let mut ctxt_2 = cks.encrypt_one_block(clear2);
|
||||
|
||||
let mut res = ctxt_1.clone();
|
||||
let mut clear = clear1;
|
||||
|
||||
res = sks.smart_block_mul_parallelized(&mut res, &ctxt_2, 0);
|
||||
res = sks.smart_block_mul_parallelized(&mut res, &mut ctxt_2, 0);
|
||||
for _ in 0..5 {
|
||||
res = sks.smart_block_mul_parallelized(&mut res, &ctxt_2, 0);
|
||||
res = sks.smart_block_mul_parallelized(&mut res, &mut ctxt_2, 0);
|
||||
clear = (clear * clear2) % modulus;
|
||||
}
|
||||
let dec: u64 = cks.decrypt(&res);
|
||||
|
||||
@@ -3,78 +3,12 @@ pub use crate::core_crypto::commons::parameters::PBSOrder;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::shortint::parameters::{CarryModulus, MessageModulus};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// This tracks the number of operations that has been done.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct Degree(pub usize);
|
||||
|
||||
impl Degree {
|
||||
pub(crate) fn after_bitxor(&self, other: Degree) -> Degree {
|
||||
let max = cmp::max(self.0, other.0);
|
||||
let min = cmp::min(self.0, other.0);
|
||||
let mut result = max;
|
||||
|
||||
//Try every possibility to find the worst case
|
||||
for i in 0..min + 1 {
|
||||
if max ^ i > result {
|
||||
result = max ^ i;
|
||||
}
|
||||
}
|
||||
|
||||
Degree(result)
|
||||
}
|
||||
|
||||
pub(crate) fn after_bitor(&self, other: Degree) -> Degree {
|
||||
let max = cmp::max(self.0, other.0);
|
||||
let min = cmp::min(self.0, other.0);
|
||||
let mut result = max;
|
||||
|
||||
for i in 0..min + 1 {
|
||||
if max | i > result {
|
||||
result = max | i;
|
||||
}
|
||||
}
|
||||
|
||||
Degree(result)
|
||||
}
|
||||
|
||||
pub(crate) fn after_bitand(&self, other: Degree) -> Degree {
|
||||
Degree(cmp::min(self.0, other.0))
|
||||
}
|
||||
|
||||
pub(crate) fn after_left_shift(&self, shift: u8, modulus: usize) -> Degree {
|
||||
let mut result = 0;
|
||||
|
||||
for i in 0..self.0 + 1 {
|
||||
let tmp = (i << shift) % modulus;
|
||||
if tmp > result {
|
||||
result = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Degree(result)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn after_pbs<F>(&self, f: F) -> Degree
|
||||
where
|
||||
F: Fn(usize) -> usize,
|
||||
{
|
||||
let mut result = 0;
|
||||
|
||||
for i in 0..self.0 + 1 {
|
||||
let tmp = f(i);
|
||||
if tmp > result {
|
||||
result = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Degree(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[must_use]
|
||||
pub struct Ciphertext {
|
||||
|
||||
@@ -11,10 +11,7 @@ use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::prelude::ContainerMut;
|
||||
use crate::core_crypto::seeders::new_seeder;
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::server_key::{
|
||||
BivariateLookupTableOwned, LookupTableMutView, LookupTableOwned,
|
||||
};
|
||||
use crate::shortint::server_key::{BivariateLookupTableOwned, LookupTableOwned};
|
||||
use crate::shortint::ServerKey;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Debug;
|
||||
@@ -32,7 +29,6 @@ thread_local! {
|
||||
}
|
||||
|
||||
pub struct BuffersRef<'a> {
|
||||
pub(crate) lookup_table: LookupTableMutView<'a>,
|
||||
// For the intermediate keyswitch result in the case of a big ciphertext
|
||||
pub(crate) buffer_lwe_after_ks: LweCiphertextMutView<'a, u64>,
|
||||
// For the intermediate PBS result in the case of a smallciphertext
|
||||
@@ -46,8 +42,6 @@ struct Memory {
|
||||
|
||||
impl Memory {
|
||||
fn as_buffers(&mut self, server_key: &ServerKey) -> BuffersRef<'_> {
|
||||
let num_elem_in_accumulator = server_key.bootstrapping_key.glwe_size().0
|
||||
* server_key.bootstrapping_key.polynomial_size().0;
|
||||
let num_elem_in_lwe_after_ks = server_key.key_switching_key.output_lwe_size().0;
|
||||
let num_elem_in_lwe_after_pbs = server_key
|
||||
.bootstrapping_key
|
||||
@@ -55,8 +49,7 @@ impl Memory {
|
||||
.to_lwe_size()
|
||||
.0;
|
||||
|
||||
let total_elem_needed =
|
||||
num_elem_in_accumulator + num_elem_in_lwe_after_ks + num_elem_in_lwe_after_pbs;
|
||||
let total_elem_needed = num_elem_in_lwe_after_ks + num_elem_in_lwe_after_pbs;
|
||||
|
||||
let all_elements = if self.buffer.len() < total_elem_needed {
|
||||
self.buffer.resize(total_elem_needed, 0u64);
|
||||
@@ -65,23 +58,8 @@ impl Memory {
|
||||
&mut self.buffer[..total_elem_needed]
|
||||
};
|
||||
|
||||
let (accumulator_elements, other_elements) =
|
||||
all_elements.split_at_mut(num_elem_in_accumulator);
|
||||
|
||||
let acc = GlweCiphertext::from_container(
|
||||
accumulator_elements,
|
||||
server_key.bootstrapping_key.polynomial_size(),
|
||||
server_key.ciphertext_modulus,
|
||||
);
|
||||
|
||||
let lookup_table = LookupTableMutView {
|
||||
acc,
|
||||
// As a safety, the degree should be updated once the accumulator is actually filled
|
||||
degree: Degree(server_key.max_degree.0),
|
||||
};
|
||||
|
||||
let (after_ks_elements, after_pbs_elements) =
|
||||
other_elements.split_at_mut(num_elem_in_lwe_after_ks);
|
||||
all_elements.split_at_mut(num_elem_in_lwe_after_ks);
|
||||
|
||||
let buffer_lwe_after_ks =
|
||||
LweCiphertextMutView::from_container(after_ks_elements, server_key.ciphertext_modulus);
|
||||
@@ -89,7 +67,6 @@ impl Memory {
|
||||
LweCiphertextMutView::from_container(after_pbs_elements, server_key.ciphertext_modulus);
|
||||
|
||||
BuffersRef {
|
||||
lookup_table,
|
||||
buffer_lwe_after_ks,
|
||||
buffer_lwe_after_pbs,
|
||||
}
|
||||
@@ -264,10 +241,7 @@ impl ShortintEngine {
|
||||
);
|
||||
let max_value = fill_accumulator(&mut acc, server_key, f);
|
||||
|
||||
Ok(LookupTableOwned {
|
||||
acc,
|
||||
degree: Degree(max_value as usize),
|
||||
})
|
||||
Ok(LookupTableOwned { acc, max_value })
|
||||
}
|
||||
|
||||
/// Generates a bivariate accumulator
|
||||
@@ -299,16 +273,13 @@ impl ShortintEngine {
|
||||
}
|
||||
|
||||
/// Return the [`BuffersRef`] and [`ComputationBuffers`] for the given `ServerKey`
|
||||
pub fn get_carry_clearing_lookup_table_and_buffers(
|
||||
pub fn get_buffers(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
) -> (BuffersRef<'_>, &mut ComputationBuffers) {
|
||||
let mut buffers = self.ciphertext_buffers.as_buffers(server_key);
|
||||
let max_degree = fill_accumulator(&mut buffers.lookup_table.acc, server_key, |n| {
|
||||
n % server_key.message_modulus.0 as u64
|
||||
});
|
||||
buffers.lookup_table.degree = Degree(max_degree as usize);
|
||||
|
||||
(buffers, &mut self.computation_buffers)
|
||||
(
|
||||
self.ciphertext_buffers.as_buffers(server_key),
|
||||
&mut self.computation_buffers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,28 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_add(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
//If the ciphertext cannot be added together without exceeding the capacity of a ciphertext
|
||||
if !server_key.is_add_possible(ct_left, ct_right) {
|
||||
if ct_left.message_modulus.0 - 1 + ct_right.degree.0 <= server_key.max_degree.0 {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
} else if ct_right.message_modulus.0 - 1 + ct_left.degree.0 <= server_key.max_degree.0 {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
} else {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(server_key.is_add_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_add_assign(server_key, &mut result, ct_right)?;
|
||||
self.unchecked_add_assign(&mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -54,6 +66,9 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(server_key.is_add_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_add_assign(ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -25,20 +25,22 @@ impl ShortintEngine {
|
||||
ct_right,
|
||||
|lhs, rhs| lhs & rhs,
|
||||
)?;
|
||||
ct_left.degree = ct_left.degree.after_bitand(ct_right.degree);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_bitand(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_bitand_assign(server_key, &mut result, ct_right)?;
|
||||
self.unchecked_bitand_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -52,6 +54,7 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitand_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -79,20 +82,22 @@ impl ShortintEngine {
|
||||
ct_right,
|
||||
|lhs, rhs| lhs ^ rhs,
|
||||
)?;
|
||||
ct_left.degree = ct_left.degree.after_bitxor(ct_right.degree);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_bitxor(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_bitxor_assign(server_key, &mut result, ct_right)?;
|
||||
self.unchecked_bitxor_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -106,6 +111,7 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitxor_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -133,20 +139,22 @@ impl ShortintEngine {
|
||||
ct_right,
|
||||
|lhs, rhs| lhs | rhs,
|
||||
)?;
|
||||
ct_left.degree = ct_left.degree.after_bitor(ct_right.degree);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_bitor(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_bitor_assign(server_key, &mut result, ct_right)?;
|
||||
self.unchecked_bitor_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -160,6 +168,7 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
self.unchecked_bitor_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -36,24 +36,14 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_greater_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_greater_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
|
||||
self.unchecked_greater_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_greater_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn unchecked_greater_or_equal(
|
||||
@@ -90,23 +80,14 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_greater_or_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_greater_or_equal_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
self.unchecked_greater_or_equal_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_greater_or_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn unchecked_less(
|
||||
@@ -143,23 +124,15 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_less_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_less_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
self.unchecked_less_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_less_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn unchecked_less_or_equal(
|
||||
@@ -196,23 +169,15 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_less_or_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_less_or_equal_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
self.unchecked_less_or_equal_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_less_or_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn unchecked_equal(
|
||||
@@ -249,29 +214,23 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_equal_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
self.unchecked_equal_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_equal(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -287,9 +246,8 @@ impl ShortintEngine {
|
||||
) -> EngineResult<()> {
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
let acc =
|
||||
self.generate_lookup_table(server_key, |x| (x % modulus == scalar as u64) as u64)?;
|
||||
self.generate_msg_lookup_table(server_key, |x| (x == scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -327,29 +285,23 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_not_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_not_equal_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
self.unchecked_not_equal_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_not_equal_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_not_equal(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -363,18 +315,19 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
let modulus: u64 = ct_left.message_modulus.0 as u64;
|
||||
let acc =
|
||||
self.generate_lookup_table(server_key, |x| (x % modulus != scalar as u64) as u64)?;
|
||||
self.generate_msg_lookup_table(server_key, |x| (x != scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_greater_or_equal(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -388,16 +341,20 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x >= scalar as u64) as u64)?;
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x >= scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_less_or_equal(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -411,16 +368,20 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x <= scalar as u64) as u64)?;
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x <= scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_greater(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -434,16 +395,20 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x > scalar as u64) as u64)?;
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x > scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_less(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
@@ -457,9 +422,11 @@ impl ShortintEngine {
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x < scalar as u64) as u64)?;
|
||||
let modulus = ct_left.message_modulus.0 as u64;
|
||||
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x < scalar as u64) as u64, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &acc)?;
|
||||
ct_left.degree.0 = 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::engine::{EngineResult, ShortintEngine};
|
||||
use crate::shortint::{Ciphertext, ServerKey};
|
||||
|
||||
@@ -39,16 +38,27 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_div(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
if ct_left.message_modulus.0 + ct_right.degree.0 <= server_key.max_degree.0 {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
} else if ct_right.message_modulus.0 + (ct_left.degree.0 + 1) <= server_key.max_degree.0
|
||||
{
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
} else {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_div_assign(server_key, &mut result, ct_right)?;
|
||||
self.unchecked_div_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -69,6 +79,8 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_div_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -97,9 +109,12 @@ impl ShortintEngine {
|
||||
scalar: u8,
|
||||
) -> EngineResult<()> {
|
||||
assert_ne!(scalar, 0, "attempt to divide by zero");
|
||||
let lookup_table = self.generate_lookup_table(server_key, |x| x / (scalar as u64))?;
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
|
||||
let lookup_table =
|
||||
self.generate_msg_lookup_table(server_key, |x| x / (scalar as u64), modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &lookup_table)?;
|
||||
ct.degree = Degree(ct.degree.0 / scalar as usize);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -124,9 +139,10 @@ impl ShortintEngine {
|
||||
modulus: u8,
|
||||
) -> EngineResult<()> {
|
||||
assert_ne!(modulus, 0);
|
||||
let acc = self.generate_lookup_table(server_key, |x| x % modulus as u64)?;
|
||||
let msg_modulus = ct.message_modulus.0 as u64;
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| x % modulus as u64, msg_modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
ct.degree = Degree(modulus as usize - 1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +315,18 @@ impl ShortintEngine {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generate_msg_lookup_table<F>(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
f: F,
|
||||
modulus: u64,
|
||||
) -> EngineResult<LookupTableOwned>
|
||||
where
|
||||
F: Fn(u64) -> u64,
|
||||
{
|
||||
Self::generate_lookup_table_with_engine(server_key, |x| f(x % modulus))
|
||||
}
|
||||
|
||||
pub(crate) fn generate_lookup_table<F>(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
@@ -326,103 +338,6 @@ impl ShortintEngine {
|
||||
Self::generate_lookup_table_with_engine(server_key, f)
|
||||
}
|
||||
|
||||
pub(crate) fn keyswitch_bootstrap_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
// Compute the programmable bootstrapping with fixed test polynomial
|
||||
let (mut ciphertext_buffers, buffers) =
|
||||
self.get_carry_clearing_lookup_table_and_buffers(server_key);
|
||||
|
||||
// Compute a keyswitch
|
||||
keyswitch_lwe_ciphertext(
|
||||
&server_key.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffers.buffer_lwe_after_ks,
|
||||
);
|
||||
|
||||
match &server_key.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic(fourier_bsk) => {
|
||||
let fft = Fft::new(fourier_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
buffers.resize(
|
||||
programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement::<u64>(
|
||||
fourier_bsk.glwe_size(),
|
||||
fourier_bsk.polynomial_size(),
|
||||
fft,
|
||||
)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
let stack = buffers.stack();
|
||||
|
||||
// Compute a bootstrap
|
||||
programmable_bootstrap_lwe_ciphertext_mem_optimized(
|
||||
&ciphertext_buffers.buffer_lwe_after_ks,
|
||||
&mut ct.ct,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
ShortintBootstrappingKey::MultiBit {
|
||||
fourier_bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => {
|
||||
if *deterministic_execution {
|
||||
multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext(
|
||||
&ciphertext_buffers.buffer_lwe_after_ks,
|
||||
&mut ct.ct,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
*thread_count,
|
||||
);
|
||||
} else {
|
||||
multi_bit_programmable_bootstrap_lwe_ciphertext(
|
||||
&ciphertext_buffers.buffer_lwe_after_ks,
|
||||
&mut ct.ct,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
*thread_count,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ct.degree = ciphertext_buffers.lookup_table.degree;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn clear_carry(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut ct_in = ct.clone();
|
||||
self.clear_carry_assign(server_key, &mut ct_in)?;
|
||||
Ok(ct_in)
|
||||
}
|
||||
|
||||
pub(crate) fn clear_carry_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
match server_key.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
self.keyswitch_bootstrap_assign(server_key, ct)?;
|
||||
}
|
||||
PBSOrder::BootstrapKeyswitch => {
|
||||
self.bootstrap_keyswitch_assign(server_key, ct)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn keyswitch_programmable_bootstrap_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
@@ -430,8 +345,7 @@ impl ShortintEngine {
|
||||
acc: &LookupTableOwned,
|
||||
) -> EngineResult<()> {
|
||||
// Compute the programmable bootstrapping with fixed test polynomial
|
||||
let (mut ciphertext_buffers, buffers) =
|
||||
self.get_carry_clearing_lookup_table_and_buffers(server_key);
|
||||
let (mut ciphertext_buffers, buffers) = self.get_buffers(server_key);
|
||||
|
||||
// Compute a key switch
|
||||
keyswitch_lwe_ciphertext(
|
||||
@@ -490,7 +404,7 @@ impl ShortintEngine {
|
||||
}
|
||||
};
|
||||
|
||||
ct.degree = acc.degree;
|
||||
ct.degree.0 = (acc.max_value as usize).max(ct.message_modulus.0 - 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -514,8 +428,7 @@ impl ShortintEngine {
|
||||
ct_right: &Ciphertext,
|
||||
acc: &BivariateLookupTableOwned,
|
||||
) -> EngineResult<()> {
|
||||
let modulus = (ct_right.degree.0 + 1) as u64;
|
||||
assert!(modulus <= acc.ct_right_modulus.0 as u64);
|
||||
// assert!(ct_right.degree.0 <= acc.ct_right_modulus.0);
|
||||
|
||||
// Message 1 is shifted
|
||||
self.unchecked_scalar_mul_assign(ct_left, acc.ct_right_modulus.0 as u8)?;
|
||||
@@ -605,9 +518,29 @@ impl ShortintEngine {
|
||||
where
|
||||
F: Fn(u64, u64) -> u64,
|
||||
{
|
||||
// Generate the lookup table for the function
|
||||
let factor = MessageModulus(ct_right.degree.0 + 1);
|
||||
let lookup_table =
|
||||
self.generate_lookup_table_bivariate_with_factor(server_key, f, factor)?;
|
||||
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
// After the message_extract, we'll have ct_left, ct_right in [0, message_modulus[
|
||||
// so the factor has to be message_modulus
|
||||
assert_eq!(ct_right.message_modulus.0, lookup_table.ct_right_modulus.0);
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut ct_res = ct_left.clone();
|
||||
|
||||
self.smart_evaluate_bivariate_function_assign(server_key, &mut ct_res, ct_right, f)?;
|
||||
self.unchecked_apply_lookup_table_bivariate_assign(
|
||||
server_key,
|
||||
&mut ct_res,
|
||||
ct_right,
|
||||
&lookup_table,
|
||||
)?;
|
||||
|
||||
Ok(ct_res)
|
||||
}
|
||||
|
||||
@@ -626,6 +559,8 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let factor = MessageModulus(ct_right.degree.0 + 1);
|
||||
|
||||
// Generate the lookup table for the function
|
||||
@@ -644,12 +579,21 @@ impl ShortintEngine {
|
||||
pub(crate) fn smart_apply_lookup_table_bivariate(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
acc: &BivariateLookupTableOwned,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
|
||||
// After the message_extract, we'll have ct_left, ct_right in [0, message_modulus[
|
||||
// so the factor has to be message_modulus
|
||||
assert_eq!(ct_right.message_modulus.0, acc.ct_right_modulus.0);
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
let mut ct_res = ct_left.clone();
|
||||
self.smart_apply_lookup_table_bivariate_assign(server_key, &mut ct_res, ct_right, acc)?;
|
||||
self.unchecked_apply_lookup_table_bivariate_assign(server_key, &mut ct_res, ct_right, acc)?;
|
||||
Ok(ct_res)
|
||||
}
|
||||
|
||||
@@ -668,6 +612,8 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
|
||||
assert!(server_key.is_functional_bivariate_pbs_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_apply_lookup_table_bivariate_assign(server_key, ct_left, ct_right, acc)
|
||||
}
|
||||
|
||||
@@ -677,8 +623,7 @@ impl ShortintEngine {
|
||||
ct: &mut Ciphertext,
|
||||
acc: &LookupTableOwned,
|
||||
) -> EngineResult<()> {
|
||||
let (mut ciphertext_buffers, buffers) =
|
||||
self.get_carry_clearing_lookup_table_and_buffers(server_key);
|
||||
let (mut ciphertext_buffers, buffers) = self.get_buffers(server_key);
|
||||
|
||||
match &server_key.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic(fourier_bsk) => {
|
||||
@@ -737,78 +682,7 @@ impl ShortintEngine {
|
||||
&mut ct.ct,
|
||||
);
|
||||
|
||||
ct.degree = acc.degree;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn bootstrap_keyswitch_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
// Compute the programmable bootstrapping with fixed test polynomial
|
||||
let (mut ciphertext_buffers, buffers) =
|
||||
self.get_carry_clearing_lookup_table_and_buffers(server_key);
|
||||
|
||||
match &server_key.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic(fourier_bsk) => {
|
||||
let fft = Fft::new(fourier_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
buffers.resize(
|
||||
programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement::<u64>(
|
||||
fourier_bsk.glwe_size(),
|
||||
fourier_bsk.polynomial_size(),
|
||||
fft,
|
||||
)
|
||||
.unwrap()
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
let stack = buffers.stack();
|
||||
|
||||
// Compute a bootstrap
|
||||
programmable_bootstrap_lwe_ciphertext_mem_optimized(
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffers.buffer_lwe_after_pbs,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
ShortintBootstrappingKey::MultiBit {
|
||||
fourier_bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => {
|
||||
if *deterministic_execution {
|
||||
multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext(
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffers.buffer_lwe_after_pbs,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
*thread_count,
|
||||
);
|
||||
} else {
|
||||
multi_bit_programmable_bootstrap_lwe_ciphertext(
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffers.buffer_lwe_after_pbs,
|
||||
&ciphertext_buffers.lookup_table.acc,
|
||||
fourier_bsk,
|
||||
*thread_count,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Compute a keyswitch
|
||||
keyswitch_lwe_ciphertext(
|
||||
&server_key.key_switching_key,
|
||||
&ciphertext_buffers.buffer_lwe_after_pbs,
|
||||
&mut ct.ct,
|
||||
);
|
||||
|
||||
ct.degree = ciphertext_buffers.lookup_table.degree;
|
||||
ct.degree.0 = (acc.max_value as usize).max(ct.message_modulus.0 - 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -819,6 +693,7 @@ impl ShortintEngine {
|
||||
ct: &mut Ciphertext,
|
||||
acc: &LookupTableOwned,
|
||||
) -> EngineResult<()> {
|
||||
// assert!(ct.degree.0 < ct.carry_modulus.0 * ct.message_modulus.0);
|
||||
match server_key.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
// This updates the ciphertext degree
|
||||
@@ -846,35 +721,20 @@ impl ShortintEngine {
|
||||
Ok(ct_res)
|
||||
}
|
||||
|
||||
pub(crate) fn apply_msg_identity_lut_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
match server_key.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
// This updates the ciphertext degree
|
||||
self.keyswitch_bootstrap_assign(server_key, ct)?;
|
||||
}
|
||||
PBSOrder::BootstrapKeyswitch => {
|
||||
// This updates the ciphertext degree
|
||||
self.bootstrap_keyswitch_assign(server_key, ct)?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn carry_extract_assign(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
if ct.carry_is_empty() {
|
||||
self.create_trivial_assign(server_key, ct, 0)?;
|
||||
} else {
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
|
||||
let lookup_table = self.generate_lookup_table(server_key, |x| x / modulus)?;
|
||||
let lookup_table = self.generate_lookup_table(server_key, |x| x / modulus)?;
|
||||
|
||||
self.apply_lookup_table_assign(server_key, ct, &lookup_table)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &lookup_table)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -894,12 +754,13 @@ impl ShortintEngine {
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
) -> EngineResult<()> {
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
if !ct.carry_is_empty() {
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
|
||||
let acc = self.generate_lookup_table(server_key, |x| x % modulus)?;
|
||||
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
let acc = self.generate_msg_lookup_table(server_key, |x| x, modulus)?;
|
||||
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::engine::{EngineResult, ShortintEngine};
|
||||
use crate::shortint::{Ciphertext, ServerKey};
|
||||
|
||||
@@ -25,23 +24,15 @@ impl ShortintEngine {
|
||||
self.create_trivial_assign(server_key, ct_left, 0)?;
|
||||
return Ok(());
|
||||
}
|
||||
let modulus = (ct_right.degree.0 + 1) as u64;
|
||||
|
||||
//message 1 is shifted to the carry bits
|
||||
self.unchecked_scalar_mul_assign(ct_left, modulus as u8)?;
|
||||
|
||||
//message 2 is placed in the message bits
|
||||
self.unchecked_add_assign(ct_left, ct_right)?;
|
||||
|
||||
//Modulus of the msg in the msg bits
|
||||
let res_modulus = ct_left.message_modulus.0 as u64;
|
||||
self.unchecked_evaluate_bivariate_function_assign(
|
||||
server_key,
|
||||
ct_left,
|
||||
ct_right,
|
||||
|x, y| (x * y) % res_modulus,
|
||||
)?;
|
||||
|
||||
let lookup_table = self.generate_lookup_table(server_key, |x| {
|
||||
((x / modulus) * (x % modulus)) % res_modulus
|
||||
})?;
|
||||
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &lookup_table)?;
|
||||
ct_left.degree = Degree(ct_left.message_modulus.0 - 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -68,25 +59,16 @@ impl ShortintEngine {
|
||||
self.create_trivial_assign(server_key, ct_left, 0)?;
|
||||
return Ok(());
|
||||
}
|
||||
let modulus = (ct_right.degree.0 + 1) as u64;
|
||||
let deg = (ct_left.degree.0 * ct_right.degree.0) / ct_right.message_modulus.0;
|
||||
|
||||
// Message 1 is shifted to the carry bits
|
||||
self.unchecked_scalar_mul_assign(ct_left, modulus as u8)?;
|
||||
|
||||
// Message 2 is placed in the message bits
|
||||
self.unchecked_add_assign(ct_left, ct_right)?;
|
||||
|
||||
// Modulus of the msg in the msg bits
|
||||
let res_modulus = server_key.message_modulus.0 as u64;
|
||||
self.unchecked_evaluate_bivariate_function_assign(
|
||||
server_key,
|
||||
ct_left,
|
||||
ct_right,
|
||||
|x, y| (x * y) / res_modulus,
|
||||
)?;
|
||||
|
||||
let lookup_table = self.generate_lookup_table(server_key, |x| {
|
||||
((x / modulus) * (x % modulus)) / res_modulus
|
||||
})?;
|
||||
|
||||
self.apply_lookup_table_assign(server_key, ct_left, &lookup_table)?;
|
||||
|
||||
ct_left.degree = Degree(deg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -137,15 +119,16 @@ impl ShortintEngine {
|
||||
) -> EngineResult<()> {
|
||||
//Choice of the multiplication algorithm depending on the parameters
|
||||
if ct_left.message_modulus.0 > ct_left.carry_modulus.0 {
|
||||
//If the ciphertext cannot be added together without exceeding the capacity of a
|
||||
//If the ciphertexts cannot be multiplied together without exceeding the capacity of a
|
||||
// ciphertext
|
||||
if !server_key.is_mul_small_carry_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_mul_small_carry_possible(ct_left, ct_right));
|
||||
self.unchecked_mul_lsb_small_carry_modulus_assign(server_key, ct_left, ct_right)?;
|
||||
} else {
|
||||
//If the ciphertext cannot be added together without exceeding the capacity of a
|
||||
//If the ciphertexts cannot be multiplied together without exceeding the capacity of a
|
||||
// ciphertext
|
||||
if !server_key.is_mul_possible(ct_left, ct_right) {
|
||||
if (server_key.message_modulus.0 - 1) * ct_right.degree.0
|
||||
@@ -161,22 +144,55 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
assert!(server_key.is_mul_small_carry_possible(ct_left, ct_right));
|
||||
self.unchecked_mul_lsb_assign(server_key, ct_left, ct_right)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_mul_lsb(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_mul_lsb_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
if ct_left.message_modulus.0 > ct_left.carry_modulus.0 {
|
||||
//If the ciphertexts cannot be multiplied together without exceeding the capacity of a
|
||||
// ciphertext
|
||||
if !server_key.is_mul_small_carry_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
assert!(server_key.is_mul_small_carry_possible(ct_left, ct_right));
|
||||
|
||||
let mut result = ct_left.clone();
|
||||
self.unchecked_mul_lsb_small_carry_modulus_assign(server_key, &mut result, ct_right)?;
|
||||
|
||||
Ok(result)
|
||||
} else {
|
||||
//If the ciphertexts cannot be multiplied together without exceeding the capacity of a
|
||||
// ciphertext
|
||||
if !server_key.is_mul_possible(ct_left, ct_right) {
|
||||
if (server_key.message_modulus.0 - 1) * ct_right.degree.0
|
||||
< (ct_right.carry_modulus.0 * ct_right.message_modulus.0 - 1)
|
||||
{
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
} else if (server_key.message_modulus.0 - 1) + ct_left.degree.0
|
||||
< (ct_right.carry_modulus.0 * ct_right.message_modulus.0 - 1)
|
||||
{
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
} else {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
}
|
||||
assert!(server_key.is_mul_small_carry_possible(ct_left, ct_right));
|
||||
let mut result = ct_left.clone();
|
||||
|
||||
self.unchecked_mul_lsb_assign(server_key, &mut result, ct_right)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn smart_mul_msb_assign(
|
||||
@@ -189,18 +205,22 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
|
||||
assert!(server_key.is_mul_possible(ct_left, ct_right));
|
||||
self.unchecked_mul_msb_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_mul_msb(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_mul_possible(ct_left, ct_right) {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
let mut result = ct_left.clone();
|
||||
self.smart_mul_msb_assign(server_key, &mut result, ct_right)?;
|
||||
Ok(result)
|
||||
|
||||
@@ -69,9 +69,15 @@ impl ShortintEngine {
|
||||
) -> EngineResult<Ciphertext> {
|
||||
// If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
|
||||
if !server_key.is_neg_possible(ct) {
|
||||
self.apply_msg_identity_lut_assign(server_key, ct)?;
|
||||
self.message_extract_assign(server_key, ct)?;
|
||||
}
|
||||
self.unchecked_neg(server_key, ct)
|
||||
assert!(server_key.is_neg_possible(ct));
|
||||
|
||||
let mut result = ct.clone();
|
||||
|
||||
self.unchecked_neg_assign(server_key, &mut result)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn smart_neg_assign(
|
||||
@@ -81,8 +87,9 @@ impl ShortintEngine {
|
||||
) -> EngineResult<()> {
|
||||
// If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
|
||||
if !server_key.is_neg_possible(ct) {
|
||||
self.apply_msg_identity_lut_assign(server_key, ct)?;
|
||||
self.message_extract_assign(server_key, ct)?;
|
||||
}
|
||||
assert!(server_key.is_neg_possible(ct));
|
||||
self.unchecked_neg_assign(server_key, ct)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,16 +45,18 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_add(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_scalar_add_possible(ct, scalar) {
|
||||
self.message_extract_assign(server_key, ct)?;
|
||||
}
|
||||
assert!(server_key.is_scalar_add_possible(ct, scalar));
|
||||
let mut ct_result = ct.clone();
|
||||
self.smart_scalar_add_assign(server_key, &mut ct_result, scalar)?;
|
||||
self.unchecked_scalar_add_assign(&mut ct_result, scalar)?;
|
||||
|
||||
Ok(ct_result)
|
||||
}
|
||||
@@ -71,9 +73,12 @@ impl ShortintEngine {
|
||||
self.unchecked_scalar_add_assign(ct, scalar)?;
|
||||
} else {
|
||||
// If the scalar is too large, PBS is used to compute the scalar mul
|
||||
let acc = self.generate_lookup_table(server_key, |x| (scalar as u64 + x) % modulus)?;
|
||||
let acc = self.generate_msg_lookup_table(
|
||||
server_key,
|
||||
|x| (scalar as u64 + x) % modulus,
|
||||
modulus,
|
||||
)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
ct.degree = Degree(server_key.message_modulus.0 - 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ impl ShortintEngine {
|
||||
lhs: &mut Ciphertext,
|
||||
rhs: u8,
|
||||
) -> EngineResult<()> {
|
||||
let lut = self.generate_lookup_table(server_key, |x| {
|
||||
let x = x % lhs.message_modulus.0 as u64;
|
||||
(x & rhs as u64) % lhs.message_modulus.0 as u64
|
||||
})?;
|
||||
let modulus = lhs.message_modulus.0 as u64;
|
||||
let lut = self.generate_msg_lookup_table(
|
||||
server_key,
|
||||
|x| (x & rhs as u64) % lhs.message_modulus.0 as u64,
|
||||
modulus,
|
||||
)?;
|
||||
self.apply_lookup_table_assign(server_key, lhs, &lut)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -67,10 +69,12 @@ impl ShortintEngine {
|
||||
lhs: &mut Ciphertext,
|
||||
rhs: u8,
|
||||
) -> EngineResult<()> {
|
||||
let lut = self.generate_lookup_table(server_key, |x| {
|
||||
let x = x % lhs.message_modulus.0 as u64;
|
||||
(x ^ rhs as u64) % lhs.message_modulus.0 as u64
|
||||
})?;
|
||||
let modulus = lhs.message_modulus.0 as u64;
|
||||
let lut = self.generate_msg_lookup_table(
|
||||
server_key,
|
||||
|x| (x ^ rhs as u64) % lhs.message_modulus.0 as u64,
|
||||
modulus,
|
||||
)?;
|
||||
self.apply_lookup_table_assign(server_key, lhs, &lut)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -115,10 +119,12 @@ impl ShortintEngine {
|
||||
lhs: &mut Ciphertext,
|
||||
rhs: u8,
|
||||
) -> EngineResult<()> {
|
||||
let lut = self.generate_lookup_table(server_key, |x| {
|
||||
let x = x % lhs.message_modulus.0 as u64;
|
||||
(x | rhs as u64) % lhs.message_modulus.0 as u64
|
||||
})?;
|
||||
let modulus = lhs.message_modulus.0 as u64;
|
||||
let lut = self.generate_msg_lookup_table(
|
||||
server_key,
|
||||
|x| (x | rhs as u64) % lhs.message_modulus.0 as u64,
|
||||
modulus,
|
||||
)?;
|
||||
self.apply_lookup_table_assign(server_key, lhs, &lut)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -41,14 +41,16 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_mul(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ctxt: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_scalar_mul_possible(ctxt, scalar) {
|
||||
self.message_extract_assign(server_key, ctxt)?;
|
||||
}
|
||||
|
||||
let mut ct_result = ctxt.clone();
|
||||
self.smart_scalar_mul_assign(server_key, &mut ct_result, scalar)?;
|
||||
|
||||
@@ -65,13 +67,15 @@ impl ShortintEngine {
|
||||
// Direct scalar computation is possible
|
||||
if server_key.is_scalar_mul_possible(ctxt, scalar) {
|
||||
self.unchecked_scalar_mul_assign(ctxt, scalar)?;
|
||||
ctxt.degree = Degree(ctxt.degree.0 * scalar as usize);
|
||||
}
|
||||
// If the ciphertext cannot be multiplied without exceeding the degree max
|
||||
else {
|
||||
let acc = self.generate_lookup_table(server_key, |x| (scalar as u64 * x) % modulus)?;
|
||||
let acc = self.generate_msg_lookup_table(
|
||||
server_key,
|
||||
|x| (scalar as u64 * x) % modulus,
|
||||
modulus,
|
||||
)?;
|
||||
self.apply_lookup_table_assign(server_key, ctxt, &acc)?;
|
||||
ctxt.degree = Degree(server_key.message_modulus.0 - 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -31,14 +31,15 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_sub(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_scalar_sub_possible(ct, scalar) {
|
||||
self.message_extract_assign(server_key, ct)?;
|
||||
}
|
||||
let mut ct_result = ct.clone();
|
||||
self.smart_scalar_sub_assign(server_key, &mut ct_result, scalar)?;
|
||||
|
||||
@@ -58,9 +59,9 @@ impl ShortintEngine {
|
||||
} else {
|
||||
let scalar = u64::from(scalar);
|
||||
// If the scalar is too large, PBS is used to compute the scalar mul
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x - scalar) % modulus)?;
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x - scalar) % modulus, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
ct.degree = Degree(server_key.message_modulus.0 - 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::engine::{EngineResult, ShortintEngine};
|
||||
use crate::shortint::{Ciphertext, ServerKey};
|
||||
|
||||
@@ -20,10 +19,10 @@ impl ShortintEngine {
|
||||
ct: &mut Ciphertext,
|
||||
shift: u8,
|
||||
) -> EngineResult<()> {
|
||||
let acc = self.generate_lookup_table(server_key, |x| x >> shift)?;
|
||||
let modulus = ct.message_modulus.0 as u64;
|
||||
let acc = self.generate_msg_lookup_table(server_key, |x| x >> shift, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
|
||||
ct.degree = Degree(ct.degree.0 >> shift);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -47,14 +46,15 @@ impl ShortintEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// by convention smart operations take mut refs to their inputs, even if they do not modify them
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
pub(crate) fn smart_scalar_left_shift(
|
||||
&mut self,
|
||||
server_key: &ServerKey,
|
||||
ct: &mut Ciphertext,
|
||||
shift: u8,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
if !server_key.is_scalar_left_shift_possible(ct, shift) {
|
||||
self.message_extract_assign(server_key, ct)?;
|
||||
}
|
||||
let mut result = ct.clone();
|
||||
self.smart_scalar_left_shift_assign(server_key, &mut result, shift)?;
|
||||
Ok(result)
|
||||
@@ -70,9 +70,9 @@ impl ShortintEngine {
|
||||
self.unchecked_scalar_left_shift_assign(ct, shift)?;
|
||||
} else {
|
||||
let modulus = server_key.message_modulus.0 as u64;
|
||||
let acc = self.generate_lookup_table(server_key, |x| (x << shift) % modulus)?;
|
||||
let acc =
|
||||
self.generate_msg_lookup_table(server_key, |x| (x << shift) % modulus, modulus)?;
|
||||
self.apply_lookup_table_assign(server_key, ct, &acc)?;
|
||||
ct.degree = ct.degree.after_left_shift(shift, modulus as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::engine::{EngineResult, ShortintEngine};
|
||||
use crate::shortint::{Ciphertext, ServerKey};
|
||||
|
||||
@@ -47,9 +45,7 @@ impl ShortintEngine {
|
||||
) -> EngineResult<u64> {
|
||||
let (neg_right, z) = self.unchecked_neg_with_correcting_term(server_key, ct_right)?;
|
||||
|
||||
lwe_ciphertext_add_assign(&mut ct_left.ct, &neg_right.ct);
|
||||
|
||||
ct_left.degree = Degree(ct_left.degree.0 + z as usize);
|
||||
self.unchecked_add_assign(ct_left, &neg_right)?;
|
||||
|
||||
Ok(z)
|
||||
}
|
||||
@@ -65,6 +61,9 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
}
|
||||
|
||||
assert!(server_key.is_sub_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_sub(server_key, ct_left, ct_right)
|
||||
}
|
||||
|
||||
@@ -80,6 +79,8 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_left)?;
|
||||
}
|
||||
|
||||
assert!(server_key.is_sub_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_sub_assign(server_key, ct_left, ct_right)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -96,6 +97,8 @@ impl ShortintEngine {
|
||||
self.message_extract_assign(server_key, ct_right)?;
|
||||
}
|
||||
|
||||
assert!(server_key.is_sub_possible(ct_left, ct_right));
|
||||
|
||||
self.unchecked_sub_with_correcting_term(server_key, ct_left, ct_right)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,8 +420,9 @@ impl ShortintEngine {
|
||||
wopbs_key: &WopbsKey,
|
||||
ct_in: &Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let modulus = ct_in.message_modulus.0 as u64;
|
||||
// First PBS to remove the noise
|
||||
let acc = self.generate_lookup_table(sks, |x| x)?;
|
||||
let acc = self.generate_msg_lookup_table(sks, |x| x, modulus)?;
|
||||
let ct_clean = self.apply_lookup_table(sks, ct_in, &acc)?;
|
||||
|
||||
let mut buffer_lwe_after_ks = LweCiphertextOwned::new(
|
||||
@@ -440,6 +441,7 @@ impl ShortintEngine {
|
||||
&mut buffer_lwe_after_ks,
|
||||
);
|
||||
|
||||
// Is this correct ?
|
||||
// The identity lut wrongly sets the max degree in the ciphertext, when in reality the
|
||||
// degree of the ciphertext has no changed, we manage this case manually here
|
||||
Ok(Ciphertext {
|
||||
@@ -456,14 +458,15 @@ impl ShortintEngine {
|
||||
wopbs_key: &WopbsKey,
|
||||
ct_in: &Ciphertext,
|
||||
) -> EngineResult<Ciphertext> {
|
||||
let modulus = ct_in.message_modulus.0 as u64;
|
||||
|
||||
// move to wopbs parameters to pbs parameters
|
||||
//Keyswitch-PBS:
|
||||
// 1. KS to go back to the original encryption key
|
||||
// 2. PBS to remove the noise added by the previous KS
|
||||
//
|
||||
let acc = self.generate_lookup_table(&wopbs_key.pbs_server_key, |x| x)?;
|
||||
let (mut ciphertext_buffers, buffers) =
|
||||
self.get_carry_clearing_lookup_table_and_buffers(&wopbs_key.pbs_server_key);
|
||||
let acc = self.generate_msg_lookup_table(&wopbs_key.pbs_server_key, |x| x, modulus)?;
|
||||
let (mut ciphertext_buffers, buffers) = self.get_buffers(&wopbs_key.pbs_server_key);
|
||||
// Compute a key switch
|
||||
keyswitch_lwe_ciphertext(
|
||||
&wopbs_key.pbs_server_key.key_switching_key,
|
||||
|
||||
@@ -107,6 +107,7 @@ impl KeySwitchingKey {
|
||||
/// ksk.cast_into(&cipher, &mut cipher_2);
|
||||
/// ```
|
||||
pub fn cast_into(&self, ct: &Ciphertext, ct_dest: &mut Ciphertext) {
|
||||
let modulus = ct_dest.carry_modulus.0;
|
||||
match self.cast_rshift {
|
||||
// Same bit size: only key switch
|
||||
0 => keyswitch_lwe_ciphertext(&self.key_switching_key, &ct.ct, &mut ct_dest.ct),
|
||||
@@ -115,14 +116,18 @@ impl KeySwitchingKey {
|
||||
i if i > 0 => {
|
||||
keyswitch_lwe_ciphertext(&self.key_switching_key, &ct.ct, &mut ct_dest.ct);
|
||||
|
||||
let acc = self.dest_server_key.generate_lookup_table(|n| n >> i);
|
||||
let acc = self
|
||||
.dest_server_key
|
||||
.generate_msg_lookup_table(|n| n >> i, modulus as u64);
|
||||
self.dest_server_key
|
||||
.apply_lookup_table_assign(ct_dest, &acc);
|
||||
}
|
||||
|
||||
// Cast to smaller bit length: left shift, then keyswitch
|
||||
i if i < 0 => {
|
||||
let acc = self.src_server_key.generate_lookup_table(|n| n << -i);
|
||||
let acc = self
|
||||
.src_server_key
|
||||
.generate_msg_lookup_table(|n| (n << -i) % modulus as u64, modulus as u64);
|
||||
let shifted_cipher = self.src_server_key.apply_lookup_table(ct, &acc);
|
||||
|
||||
keyswitch_lwe_ciphertext(
|
||||
|
||||
@@ -118,13 +118,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
|
||||
@@ -114,13 +114,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -538,13 +538,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -966,13 +966,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
|
||||
@@ -65,14 +65,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -279,14 +279,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -505,14 +505,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -721,14 +721,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -945,14 +945,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -1159,14 +1159,14 @@ impl ServerKey {
|
||||
let lhs = if ct_left.carry_is_empty() {
|
||||
ct_left
|
||||
} else {
|
||||
tmp_lhs = self.clear_carry(ct_left);
|
||||
tmp_lhs = self.message_extract(ct_left);
|
||||
&tmp_lhs
|
||||
};
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -1358,7 +1358,7 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 == scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.smart_scalar_equal(self, ct_left, scalar).unwrap()
|
||||
})
|
||||
@@ -1373,7 +1373,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_equal(ct_left, scalar)
|
||||
}
|
||||
|
||||
@@ -1412,7 +1412,7 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 != scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_not_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_not_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine
|
||||
.smart_scalar_not_equal(self, ct_left, scalar)
|
||||
@@ -1429,7 +1429,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_not_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_not_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_not_equal(ct_left, scalar)
|
||||
}
|
||||
|
||||
@@ -1469,7 +1469,11 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 >= scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_greater_or_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_greater_or_equal(
|
||||
&self,
|
||||
ct_left: &mut Ciphertext,
|
||||
scalar: u8,
|
||||
) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine
|
||||
.smart_scalar_greater_or_equal(self, ct_left, scalar)
|
||||
@@ -1487,7 +1491,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_greater_or_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_greater_or_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_greater_or_equal(ct_left, scalar)
|
||||
}
|
||||
|
||||
@@ -1527,7 +1531,7 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 <= scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_less_or_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_less_or_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine
|
||||
.smart_scalar_less_or_equal(self, ct_left, scalar)
|
||||
@@ -1545,7 +1549,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_less_or_equal(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_less_or_equal(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_less_or_equal(ct_left, scalar)
|
||||
}
|
||||
|
||||
@@ -1584,7 +1588,7 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 > scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_greater(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_greater(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.smart_scalar_greater(self, ct_left, scalar).unwrap()
|
||||
})
|
||||
@@ -1599,7 +1603,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_greater(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_greater(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_greater(ct_left, scalar)
|
||||
}
|
||||
|
||||
@@ -1638,7 +1642,7 @@ impl ServerKey {
|
||||
/// let res = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(res, (msg_1 < scalar as u64) as u64);
|
||||
/// ```
|
||||
pub fn smart_scalar_less(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn smart_scalar_less(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.smart_scalar_less(self, ct_left, scalar).unwrap()
|
||||
})
|
||||
@@ -1653,7 +1657,7 @@ impl ServerKey {
|
||||
/// This means that when using only "default" operations, a given operation (like add for
|
||||
/// example) has always the same performance characteristics from one call to another and
|
||||
/// guarantees correctness by pre-emptively clearing carries of output ciphertexts.
|
||||
pub fn scalar_less(&self, ct_left: &Ciphertext, scalar: u8) -> Ciphertext {
|
||||
pub fn scalar_less(&self, ct_left: &mut Ciphertext, scalar: u8) -> Ciphertext {
|
||||
self.smart_scalar_less(ct_left, scalar)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +123,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::core_crypto::commons::parameters::{
|
||||
};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::shortint::ciphertext::{Ciphertext, Degree};
|
||||
use crate::shortint::ciphertext::Ciphertext;
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::parameters::{CarryModulus, CiphertextModulus, MessageModulus};
|
||||
@@ -274,7 +274,7 @@ fn ciphertexts_can_be_packed_without_exceeding_space(
|
||||
#[must_use]
|
||||
pub struct LookupTable<C: Container<Element = u64>> {
|
||||
pub acc: GlweCiphertext<C>,
|
||||
pub degree: Degree,
|
||||
pub max_value: u64,
|
||||
}
|
||||
|
||||
pub type LookupTableOwned = LookupTable<Vec<u64>>;
|
||||
@@ -363,6 +363,15 @@ impl ServerKey {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_msg_lookup_table<F>(&self, f: F, modulus: u64) -> LookupTableOwned
|
||||
where
|
||||
F: Fn(u64) -> u64,
|
||||
{
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.generate_msg_lookup_table(self, f, modulus).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_lookup_table_bivariate_with_factor<F>(
|
||||
&self,
|
||||
f: F,
|
||||
@@ -392,14 +401,14 @@ impl ServerKey {
|
||||
/// let msg_1 = 3;
|
||||
/// let msg_2 = 2;
|
||||
///
|
||||
/// let ct1 = cks.encrypt(msg_1);
|
||||
/// let mut ct1 = cks.encrypt(msg_1);
|
||||
/// let mut ct2 = cks.encrypt(msg_2);
|
||||
///
|
||||
/// let f = |x, y| (x + y) % 4;
|
||||
///
|
||||
/// let acc = sks.generate_lookup_table_bivariate(f);
|
||||
/// assert!(acc.is_bivariate_pbs_possible(&ct1, &ct2));
|
||||
/// let ct_res = sks.smart_apply_lookup_table_bivariate(&ct1, &mut ct2, &acc);
|
||||
/// let ct_res = sks.smart_apply_lookup_table_bivariate(&mut ct1, &mut ct2, &acc);
|
||||
///
|
||||
/// let dec = cks.decrypt(&ct_res);
|
||||
/// assert_eq!(dec, f(msg_1, msg_2));
|
||||
@@ -413,60 +422,6 @@ impl ServerKey {
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute a keyswitch and a bootstrap, returning a new ciphertext with empty
|
||||
/// carry bits.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::shortint::gen_keys;
|
||||
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
|
||||
///
|
||||
/// // Generate the client key and the server key:
|
||||
/// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
|
||||
///
|
||||
/// let mut ct1 = cks.encrypt(3);
|
||||
/// // | ct1 |
|
||||
/// // | carry | message |
|
||||
/// // |-------|---------|
|
||||
/// // | 0 0 | 1 1 |
|
||||
/// let mut ct2 = cks.encrypt(2);
|
||||
/// // | ct2 |
|
||||
/// // | carry | message |
|
||||
/// // |-------|---------|
|
||||
/// // | 0 0 | 1 0 |
|
||||
///
|
||||
/// let ct_res = sks.smart_add(&mut ct1, &mut ct2);
|
||||
/// // | ct_res |
|
||||
/// // | carry | message |
|
||||
/// // |-------|---------|
|
||||
/// // | 0 1 | 0 1 |
|
||||
///
|
||||
/// // Get the carry
|
||||
/// let ct_carry = sks.carry_extract(&ct_res);
|
||||
/// let carry = cks.decrypt(&ct_carry);
|
||||
/// assert_eq!(carry, 1);
|
||||
///
|
||||
/// let ct_res = sks.clear_carry(&ct_res);
|
||||
///
|
||||
/// let ct_carry = sks.carry_extract(&ct_res);
|
||||
/// let carry = cks.decrypt(&ct_carry);
|
||||
/// assert_eq!(carry, 0);
|
||||
///
|
||||
/// let clear = cks.decrypt(&ct_res);
|
||||
///
|
||||
/// assert_eq!(clear, (3 + 2) % 4);
|
||||
/// ```
|
||||
pub fn clear_carry(&self, ct_in: &Ciphertext) -> Ciphertext {
|
||||
ShortintEngine::with_thread_local_mut(|engine| engine.clear_carry(self, ct_in).unwrap())
|
||||
}
|
||||
|
||||
pub fn clear_carry_assign(&self, ct_in: &mut Ciphertext) {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.clear_carry_assign(self, ct_in).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute a keyswitch and programmable bootstrap.
|
||||
///
|
||||
/// # Example
|
||||
@@ -529,13 +484,13 @@ impl ServerKey {
|
||||
/// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
|
||||
///
|
||||
/// let msg: u64 = 3;
|
||||
/// let ct1 = cks.encrypt(msg);
|
||||
/// let mut ct1 = cks.encrypt(msg);
|
||||
/// let mut ct2 = cks.encrypt(msg);
|
||||
/// let modulus = cks.parameters.message_modulus().0 as u64;
|
||||
///
|
||||
/// // Generate the lookup table for the function f: x -> x^3 mod 2^2
|
||||
/// let acc = sks.generate_lookup_table_bivariate(|x, y| x * y * x % modulus);
|
||||
/// let ct_res = sks.smart_apply_lookup_table_bivariate(&ct1, &mut ct2, &acc);
|
||||
/// let ct_res = sks.smart_apply_lookup_table_bivariate(&mut ct1, &mut ct2, &acc);
|
||||
///
|
||||
/// let dec = cks.decrypt(&ct_res);
|
||||
/// // 3^3 mod 4 = 3
|
||||
@@ -543,7 +498,7 @@ impl ServerKey {
|
||||
/// ```
|
||||
pub fn smart_apply_lookup_table_bivariate(
|
||||
&self,
|
||||
ct_left: &Ciphertext,
|
||||
ct_left: &mut Ciphertext,
|
||||
ct_right: &mut Ciphertext,
|
||||
acc: &BivariateLookupTableOwned,
|
||||
) -> Ciphertext {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::ServerKey;
|
||||
use crate::shortint::ciphertext::Degree;
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::server_key::CheckError;
|
||||
use crate::shortint::server_key::CheckError::CarryFull;
|
||||
@@ -631,8 +630,7 @@ impl ServerKey {
|
||||
ct_right: &Ciphertext,
|
||||
) -> Result<Ciphertext, CheckError> {
|
||||
if self.is_mul_small_carry_possible(ct_left, ct_right) {
|
||||
let mut ct_result = self.unchecked_mul_lsb_small_carry(ct_left, ct_right);
|
||||
ct_result.degree = Degree(ct_left.degree.0 * 2);
|
||||
let ct_result = self.unchecked_mul_lsb_small_carry(ct_left, ct_right);
|
||||
Ok(ct_result)
|
||||
} else {
|
||||
Err(CarryFull)
|
||||
@@ -857,13 +855,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
@@ -873,7 +871,7 @@ impl ServerKey {
|
||||
.unchecked_mul_lsb_small_carry_modulus_assign(self, ct_left, rhs)
|
||||
.unwrap()
|
||||
});
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
} else {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine.unchecked_mul_lsb_assign(self, ct_left, rhs).unwrap()
|
||||
@@ -996,13 +994,13 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ impl ServerKey {
|
||||
/// ```
|
||||
pub fn neg_assign(&self, ct: &mut Ciphertext) {
|
||||
if !ct.carry_is_empty() {
|
||||
self.clear_carry_assign(ct);
|
||||
self.message_extract_assign(ct);
|
||||
}
|
||||
self.unchecked_neg_assign(ct);
|
||||
self.clear_carry_assign(ct);
|
||||
self.message_extract_assign(ct);
|
||||
}
|
||||
|
||||
/// Homomorphically negates a message without checks.
|
||||
|
||||
@@ -36,7 +36,7 @@ impl ServerKey {
|
||||
|
||||
pub fn scalar_bitand_assign(&self, lhs: &mut Ciphertext, rhs: u8) {
|
||||
if !lhs.carry_is_empty() {
|
||||
self.clear_carry_assign(lhs);
|
||||
self.message_extract_assign(lhs);
|
||||
}
|
||||
|
||||
self.unchecked_scalar_bitand_assign(lhs, rhs);
|
||||
@@ -103,7 +103,7 @@ impl ServerKey {
|
||||
|
||||
pub fn scalar_bitxor_assign(&self, lhs: &mut Ciphertext, rhs: u8) {
|
||||
if !lhs.carry_is_empty() {
|
||||
self.clear_carry_assign(lhs);
|
||||
self.message_extract_assign(lhs);
|
||||
}
|
||||
|
||||
self.unchecked_scalar_bitxor_assign(lhs, rhs);
|
||||
@@ -169,7 +169,7 @@ impl ServerKey {
|
||||
|
||||
pub fn scalar_bitor_assign(&self, lhs: &mut Ciphertext, rhs: u8) {
|
||||
if !lhs.carry_is_empty() {
|
||||
self.clear_carry_assign(lhs);
|
||||
self.message_extract_assign(lhs);
|
||||
}
|
||||
|
||||
self.unchecked_scalar_bitor_assign(lhs, rhs);
|
||||
|
||||
@@ -113,7 +113,7 @@ impl ServerKey {
|
||||
/// ```
|
||||
pub fn scalar_mul_assign(&self, ct: &mut Ciphertext, scalar: u8) {
|
||||
let modulus = self.message_modulus.0 as u64;
|
||||
let acc = self.generate_lookup_table(|x| (scalar as u64 * x) % modulus);
|
||||
let acc = self.generate_msg_lookup_table(|x| (scalar as u64 * x) % modulus, modulus);
|
||||
self.apply_lookup_table_assign(ct, &acc);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ impl ServerKey {
|
||||
/// ```
|
||||
pub fn scalar_right_shift_assign(&self, ct: &mut Ciphertext, shift: u8) {
|
||||
let modulus = self.message_modulus.0 as u64;
|
||||
let acc = self.generate_lookup_table(|x| (x >> shift) % modulus);
|
||||
let acc = self.generate_msg_lookup_table(|x| x >> shift, modulus);
|
||||
self.apply_lookup_table_assign(ct, &acc);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,18 +77,18 @@ impl ServerKey {
|
||||
let tmp_rhs: Ciphertext;
|
||||
|
||||
if !ct_left.carry_is_empty() {
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
let rhs = if ct_right.carry_is_empty() {
|
||||
ct_right
|
||||
} else {
|
||||
tmp_rhs = self.clear_carry(ct_right);
|
||||
tmp_rhs = self.message_extract(ct_right);
|
||||
&tmp_rhs
|
||||
};
|
||||
|
||||
self.unchecked_sub_assign(ct_left, rhs);
|
||||
self.clear_carry_assign(ct_left);
|
||||
self.message_extract_assign(ct_left);
|
||||
}
|
||||
|
||||
/// Homomorphically subtracts ct_right to ct_left.
|
||||
|
||||
@@ -310,7 +310,7 @@ where
|
||||
let ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
// keyswitch and bootstrap
|
||||
let ct_res = sks.clear_carry(&ctxt_0);
|
||||
let ct_res = sks.message_extract(&ctxt_0);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
@@ -344,7 +344,7 @@ where
|
||||
let ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
//define the lookup_table as identity
|
||||
let acc = sks.generate_lookup_table(|n| n % modulus);
|
||||
let acc = sks.generate_msg_lookup_table(|n| n, modulus);
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.apply_lookup_table(&ctxt_0, &acc);
|
||||
|
||||
@@ -1919,10 +1919,10 @@ where
|
||||
let scalar = (rng.gen::<u16>() % modulus as u16) as u8;
|
||||
|
||||
// encryption of an integer
|
||||
let ctxt = cks.encrypt(clear);
|
||||
let mut ctxt = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.smart_scalar_equal(&ctxt, scalar);
|
||||
let ct_res = sks.smart_scalar_equal(&mut ctxt, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
@@ -1951,10 +1951,10 @@ where
|
||||
let scalar = (rng.gen::<u16>() % modulus as u16) as u8;
|
||||
|
||||
// encryption of an integer
|
||||
let ctxt = cks.encrypt(clear);
|
||||
let mut ctxt = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.smart_scalar_less(&ctxt, scalar);
|
||||
let ct_res = sks.smart_scalar_less(&mut ctxt, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
@@ -1983,10 +1983,10 @@ where
|
||||
let scalar = (rng.gen::<u16>() % modulus as u16) as u8;
|
||||
|
||||
// encryption of an integer
|
||||
let ctxt = cks.encrypt(clear);
|
||||
let mut ctxt = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.smart_scalar_less_or_equal(&ctxt, scalar);
|
||||
let ct_res = sks.smart_scalar_less_or_equal(&mut ctxt, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
@@ -2015,10 +2015,10 @@ where
|
||||
let scalar = (rng.gen::<u16>() % modulus as u16) as u8;
|
||||
|
||||
// encryption of an integer
|
||||
let ctxt = cks.encrypt(clear);
|
||||
let mut ctxt = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.smart_scalar_greater(&ctxt, scalar);
|
||||
let ct_res = sks.smart_scalar_greater(&mut ctxt, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
@@ -2047,10 +2047,10 @@ where
|
||||
let scalar = (rng.gen::<u16>() % modulus as u16) as u8;
|
||||
|
||||
// encryption of an integer
|
||||
let ctxt = cks.encrypt(clear);
|
||||
let mut ctxt = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.smart_scalar_greater_or_equal(&ctxt, scalar);
|
||||
let ct_res = sks.smart_scalar_greater_or_equal(&mut ctxt, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res = cks.decrypt(&ct_res);
|
||||
|
||||
Reference in New Issue
Block a user