mirror of
https://github.com/arnaucube/phantom-zone.git
synced 2026-01-18 20:01:34 +01:00
add APIs for unsigned
This commit is contained in:
262
src/shortint/mod.rs
Normal file
262
src/shortint/mod.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
bool::evaluator::{BoolEvaluator, ClientKey, ServerKeyEvaluationDomain, BOOL_SERVER_KEY},
|
||||
utils::{Global, WithLocal},
|
||||
Decryptor, Encryptor,
|
||||
};
|
||||
use ops::{
|
||||
arbitrary_bit_adder, arbitrary_bit_division_for_quotient_and_rem, arbitrary_bit_subtractor,
|
||||
eight_bit_mul,
|
||||
};
|
||||
|
||||
mod ops;
|
||||
mod types;
|
||||
|
||||
type FheUint8 = types::FheUint8<Vec<u64>>;
|
||||
|
||||
fn add_mut(a: &mut FheUint8, b: &FheUint8) {
|
||||
BoolEvaluator::with_local_mut_mut(&mut |e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
arbitrary_bit_adder(e, a.data_mut(), b.data(), false, key);
|
||||
});
|
||||
}
|
||||
|
||||
fn sub(a: &FheUint8, b: &FheUint8) -> FheUint8 {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let (out, _, _) = arbitrary_bit_subtractor(e, a.data(), b.data(), key);
|
||||
FheUint8 { data: out }
|
||||
})
|
||||
}
|
||||
|
||||
fn mul(a: &FheUint8, b: &FheUint8) -> FheUint8 {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let out = eight_bit_mul(e, a.data(), b.data(), key);
|
||||
FheUint8 { data: out }
|
||||
})
|
||||
}
|
||||
|
||||
fn div(a: &FheUint8, b: &FheUint8) -> (FheUint8, FheUint8) {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let (quotient, remainder) =
|
||||
arbitrary_bit_division_for_quotient_and_rem(e, a.data(), b.data(), key);
|
||||
|
||||
(FheUint8 { data: quotient }, FheUint8 { data: remainder })
|
||||
})
|
||||
}
|
||||
|
||||
impl Encryptor<u8, FheUint8> for ClientKey {
|
||||
fn encrypt(&self, m: &u8) -> FheUint8 {
|
||||
let cts = (0..8)
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let bit = ((m >> i) & 1) == 1;
|
||||
Encryptor::<bool, Vec<u64>>::encrypt(self, &bit)
|
||||
})
|
||||
.collect_vec();
|
||||
FheUint8 { data: cts }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decryptor<u8, FheUint8> for ClientKey {
|
||||
fn decrypt(&self, c: &FheUint8) -> u8 {
|
||||
let mut out = 0u8;
|
||||
c.data().iter().enumerate().for_each(|(index, bit_c)| {
|
||||
let bool = Decryptor::<bool, Vec<u64>>::decrypt(self, bit_c);
|
||||
if bool {
|
||||
out += 1 << index;
|
||||
}
|
||||
});
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
mod frontend {
|
||||
use super::ops::{
|
||||
arbitrary_bit_adder, arbitrary_bit_division_for_quotient_and_rem, arbitrary_bit_subtractor,
|
||||
eight_bit_mul,
|
||||
};
|
||||
use crate::{
|
||||
bool::evaluator::{BoolEvaluator, ServerKeyEvaluationDomain},
|
||||
utils::{Global, WithLocal},
|
||||
};
|
||||
|
||||
use super::{add_mut, div, mul, FheUint8};
|
||||
|
||||
mod arithetic {
|
||||
use super::*;
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Rem, Sub};
|
||||
|
||||
impl AddAssign<&FheUint8> for FheUint8 {
|
||||
fn add_assign(&mut self, rhs: &FheUint8) {
|
||||
BoolEvaluator::with_local_mut_mut(&mut |e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
arbitrary_bit_adder(e, self.data_mut(), rhs.data(), false, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&FheUint8> for &FheUint8 {
|
||||
type Output = FheUint8;
|
||||
fn add(self, rhs: &FheUint8) -> Self::Output {
|
||||
let mut a = self.clone();
|
||||
a += rhs;
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<&FheUint8> for &FheUint8 {
|
||||
type Output = FheUint8;
|
||||
fn sub(self, rhs: &FheUint8) -> Self::Output {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let (out, _, _) = arbitrary_bit_subtractor(e, self.data(), self.data(), key);
|
||||
FheUint8 { data: out }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<&FheUint8> for &FheUint8 {
|
||||
type Output = FheUint8;
|
||||
fn mul(self, rhs: &FheUint8) -> Self::Output {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let out = eight_bit_mul(e, self.data(), rhs.data(), key);
|
||||
FheUint8 { data: out }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<&FheUint8> for &FheUint8 {
|
||||
type Output = FheUint8;
|
||||
fn div(self, rhs: &FheUint8) -> Self::Output {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let (quotient, _) = arbitrary_bit_division_for_quotient_and_rem(
|
||||
e,
|
||||
self.data(),
|
||||
rhs.data(),
|
||||
key,
|
||||
);
|
||||
FheUint8 { data: quotient }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<&FheUint8> for &FheUint8 {
|
||||
type Output = FheUint8;
|
||||
fn rem(self, rhs: &FheUint8) -> Self::Output {
|
||||
BoolEvaluator::with_local_mut(|e| {
|
||||
let key = ServerKeyEvaluationDomain::global();
|
||||
let (_, remainder) = arbitrary_bit_division_for_quotient_and_rem(
|
||||
e,
|
||||
self.data(),
|
||||
rhs.data(),
|
||||
key,
|
||||
);
|
||||
FheUint8 { data: remainder }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod booleans {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_traits::Euclid;
|
||||
|
||||
use crate::{
|
||||
bool::{
|
||||
evaluator::{gen_keys, set_parameter_set, BoolEvaluator},
|
||||
parameters::SP_BOOL_PARAMS,
|
||||
},
|
||||
shortint::{add_mut, div, mul, sub, types::FheUint8},
|
||||
Decryptor, Encryptor,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn qwerty() {
|
||||
set_parameter_set(&SP_BOOL_PARAMS);
|
||||
|
||||
let (ck, sk) = gen_keys();
|
||||
sk.set_server_key();
|
||||
|
||||
for i in 1..=255 {
|
||||
for j in 0..=255 {
|
||||
let m0 = i;
|
||||
let m1 = j;
|
||||
let c0 = ck.encrypt(&m0);
|
||||
let c1 = ck.encrypt(&m1);
|
||||
|
||||
assert!(ck.decrypt(&c0) == m0);
|
||||
assert!(ck.decrypt(&c1) == m1);
|
||||
|
||||
// Add
|
||||
// let mut c_m0_plus_m1 = FheUint8 {
|
||||
// data: c0.data().to_vec(),
|
||||
// };
|
||||
// add_mut(&mut c_m0_plus_m1, &c1);
|
||||
// let m0_plus_m1 = ck.decrypt(&c_m0_plus_m1);
|
||||
// assert_eq!(
|
||||
// m0_plus_m1,
|
||||
// m0.wrapping_add(m1),
|
||||
// "Expected {} but got {m0_plus_m1} for {i}+{j}",
|
||||
// m0.wrapping_add(m1)
|
||||
// );
|
||||
|
||||
// Sub
|
||||
// let c_sub = sub(&c0, &c1);
|
||||
// let m0_sub_m1 = ck.decrypt(&c_sub);
|
||||
// dbg!(m0, m1, m0_sub_m1);
|
||||
// assert_eq!(
|
||||
// m0_sub_m1,
|
||||
// m0.wrapping_sub(m1),
|
||||
// "Expected {} but got {m0_sub_m1} for {i}-{j}",
|
||||
// m0.wrapping_sub(m1)
|
||||
// );
|
||||
|
||||
// Mul
|
||||
// let c_m0m1 = mul(&c0, &c1);
|
||||
// let m0m1 = ck.decrypt(&c_m0m1);
|
||||
// assert_eq!(
|
||||
// m0m1,
|
||||
// m0.wrapping_mul(m1),
|
||||
// "Expected {} but got {m0m1} for {i}x{j}",
|
||||
// m0.wrapping_mul(m1)
|
||||
// );
|
||||
|
||||
// Div
|
||||
// let (c_quotient, c_rem) = div(&c0, &c1);
|
||||
// let m_quotient = ck.decrypt(&c_quotient);
|
||||
// let m_remainder = ck.decrypt(&c_rem);
|
||||
// if j != 0 {
|
||||
// let (q, r) = i.div_rem_euclid(&j);
|
||||
// assert_eq!(
|
||||
// m_quotient, q,
|
||||
// "Expected {} but got {m_quotient} for {i}/{j}",
|
||||
// q
|
||||
// );
|
||||
// assert_eq!(
|
||||
// m_remainder, r,
|
||||
// "Expected {} but got {m_quotient} for {i}%{j}",
|
||||
// r
|
||||
// );
|
||||
// } else {
|
||||
// assert_eq!(
|
||||
// m_quotient, 255,
|
||||
// "Expected 255 but got {m_quotient}. Case div by zero"
|
||||
// );
|
||||
// assert_eq!(
|
||||
// m_remainder, i,
|
||||
// "Expected {i} but got {m_quotient}. Case div by zero"
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
src/shortint/ops.rs
Normal file
362
src/shortint/ops.rs
Normal file
@@ -0,0 +1,362 @@
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use itertools::{izip, Itertools};
|
||||
use num_traits::PrimInt;
|
||||
|
||||
use crate::{
|
||||
backend::ModularOpsU64,
|
||||
bool::{
|
||||
evaluator::{BoolEvaluator, BooleanGates, ClientKey, ServerKeyEvaluationDomain},
|
||||
parameters::CiphertextModulus,
|
||||
},
|
||||
ntt::NttBackendU64,
|
||||
random::DefaultSecureRng,
|
||||
Decryptor,
|
||||
};
|
||||
|
||||
pub(super) fn half_adder<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &mut E::Ciphertext,
|
||||
b: &E::Ciphertext,
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
let carry = evaluator.and(a, b, key);
|
||||
evaluator.xor_inplace(a, b, key);
|
||||
carry
|
||||
}
|
||||
|
||||
pub(super) fn full_adder_plain_carry_in<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &mut E::Ciphertext,
|
||||
b: &E::Ciphertext,
|
||||
carry_in: bool,
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
let mut a_and_b = evaluator.and(a, b, key);
|
||||
evaluator.xor_inplace(a, b, key); //a = a ^ b
|
||||
if carry_in {
|
||||
// a_and_b = A & B | ((A^B) & C_in={True})
|
||||
evaluator.or_inplace(&mut a_and_b, &a, key);
|
||||
} else {
|
||||
// a_and_b = A & B | ((A^B) & C_in={False})
|
||||
// a_and_b = A & B
|
||||
// noop
|
||||
}
|
||||
|
||||
// In xor if a input is 0, output equals the firt variable. If input is 1 then
|
||||
// output equals !(first variable)
|
||||
if carry_in {
|
||||
// (A^B)^1 = !(A^B)
|
||||
evaluator.not_inplace(a);
|
||||
} else {
|
||||
// (A^B)^0
|
||||
// no-op
|
||||
}
|
||||
a_and_b
|
||||
}
|
||||
|
||||
pub(super) fn full_adder<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &mut E::Ciphertext,
|
||||
b: &E::Ciphertext,
|
||||
carry_in: &E::Ciphertext,
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
let mut a_and_b = evaluator.and(a, b, key);
|
||||
evaluator.xor_inplace(a, b, key); //a = a ^ b
|
||||
let a_xor_b_and_c = evaluator.and(&a, carry_in, key);
|
||||
evaluator.or_inplace(&mut a_and_b, &a_xor_b_and_c, key); // a_and_b = A & B | ((A^B) & C_in)
|
||||
evaluator.xor_inplace(a, &carry_in, key);
|
||||
a_and_b
|
||||
}
|
||||
|
||||
pub(super) fn arbitrary_bit_adder<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &mut [E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
carry_in: bool,
|
||||
key: &E::Key,
|
||||
) -> (E::Ciphertext, E::Ciphertext)
|
||||
where
|
||||
E::Ciphertext: Clone,
|
||||
{
|
||||
assert!(a.len() == b.len());
|
||||
let n = a.len();
|
||||
|
||||
let mut carry = if !carry_in {
|
||||
half_adder(evaluator, &mut a[0], &b[0], key)
|
||||
} else {
|
||||
full_adder_plain_carry_in(evaluator, &mut a[0], &b[0], true, key)
|
||||
};
|
||||
|
||||
izip!(a.iter_mut(), b.iter())
|
||||
.skip(1)
|
||||
.take(n - 3)
|
||||
.for_each(|(a_bit, b_bit)| {
|
||||
carry = full_adder(evaluator, a_bit, b_bit, &carry, key);
|
||||
});
|
||||
|
||||
let carry_last_last = full_adder(evaluator, &mut a[n - 2], &b[n - 2], &carry, key);
|
||||
let carry_last = full_adder(evaluator, &mut a[n - 1], &b[n - 1], &carry_last_last, key);
|
||||
|
||||
(carry_last, carry_last_last)
|
||||
}
|
||||
|
||||
pub(super) fn arbitrary_bit_subtractor<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> (Vec<E::Ciphertext>, E::Ciphertext, E::Ciphertext)
|
||||
where
|
||||
E::Ciphertext: Clone,
|
||||
{
|
||||
let mut neg_b: Vec<E::Ciphertext> = b.iter().map(|v| evaluator.not(v)).collect();
|
||||
let (carry_last, carry_last_last) = arbitrary_bit_adder(evaluator, &mut neg_b, &a, true, key);
|
||||
return (neg_b, carry_last, carry_last_last);
|
||||
}
|
||||
|
||||
pub(super) fn bit_mux<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
selector: E::Ciphertext,
|
||||
if_true: &E::Ciphertext,
|
||||
if_false: &E::Ciphertext,
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
// (s&a) | ((1-s)^b)
|
||||
let not_selector = evaluator.not(&selector);
|
||||
|
||||
let s_and_a = evaluator.and(&selector, if_true, key);
|
||||
let s_and_b = evaluator.and(¬_selector, if_false, key);
|
||||
evaluator.or(&s_and_a, &s_and_b, key)
|
||||
}
|
||||
|
||||
pub(super) fn arbitrary_bit_mux<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
selector: &E::Ciphertext,
|
||||
if_true: &[E::Ciphertext],
|
||||
if_false: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> Vec<E::Ciphertext> {
|
||||
// (s&a) | ((1-s)^b)
|
||||
let not_selector = evaluator.not(&selector);
|
||||
|
||||
izip!(if_true.iter(), if_false.iter())
|
||||
.map(|(a, b)| {
|
||||
let s_and_a = evaluator.and(&selector, a, key);
|
||||
let s_and_b = evaluator.and(¬_selector, b, key);
|
||||
evaluator.or(&s_and_a, &s_and_b, key)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn eight_bit_mul<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> Vec<E::Ciphertext> {
|
||||
assert!(a.len() == 8);
|
||||
assert!(b.len() == 8);
|
||||
let mut carries = Vec::with_capacity(7);
|
||||
let mut out = Vec::with_capacity(8);
|
||||
|
||||
for i in (0..8) {
|
||||
if i == 0 {
|
||||
let s = evaluator.and(&a[0], &b[0], key);
|
||||
out.push(s);
|
||||
} else if i == 1 {
|
||||
let mut tmp0 = evaluator.and(&a[1], &b[0], key);
|
||||
let tmp1 = evaluator.and(&a[0], &b[1], key);
|
||||
let carry = half_adder(evaluator, &mut tmp0, &tmp1, key);
|
||||
carries.push(carry);
|
||||
out.push(tmp0);
|
||||
} else {
|
||||
let mut sum = {
|
||||
let mut sum = evaluator.and(&a[i], &b[0], key);
|
||||
let tmp = evaluator.and(&a[i - 1], &b[1], key);
|
||||
carries[0] = full_adder(evaluator, &mut sum, &tmp, &carries[0], key);
|
||||
sum
|
||||
};
|
||||
|
||||
for j in 2..i {
|
||||
let tmp = evaluator.and(&a[i - j], &b[j], key);
|
||||
carries[j - 1] = full_adder(evaluator, &mut sum, &tmp, &carries[j - 1], key);
|
||||
}
|
||||
|
||||
let tmp = evaluator.and(&a[0], &b[i], key);
|
||||
let carry = half_adder(evaluator, &mut sum, &tmp, key);
|
||||
carries.push(carry);
|
||||
|
||||
out.push(sum)
|
||||
}
|
||||
debug_assert!(carries.len() <= 7);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub(super) fn arbitrary_bit_division_for_quotient_and_rem<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> (Vec<E::Ciphertext>, Vec<E::Ciphertext>)
|
||||
where
|
||||
E::Ciphertext: Clone,
|
||||
{
|
||||
let n = a.len();
|
||||
let neg_b = b.iter().map(|v| evaluator.not(v)).collect_vec();
|
||||
|
||||
// Both remainder and quotient are initially stored in Big-endian in contract to
|
||||
// the usual little endian we use. This is more friendly to vec pushes in
|
||||
// division. After computing remainder and quotient, we simply reverse the
|
||||
// vectors.
|
||||
let mut remainder = vec![];
|
||||
let mut quotient = vec![];
|
||||
for i in 0..n {
|
||||
// left shift
|
||||
remainder.push(a[n - 1 - i].clone());
|
||||
|
||||
let mut subtract = remainder.clone();
|
||||
|
||||
// subtraction
|
||||
// At i^th iteration remainder is only filled with i bits and the rest of the
|
||||
// bits are zero. For example, at i = 1
|
||||
// 0 0 0 0 0 0 X X => remainder
|
||||
// - Y Y Y Y Y Y Y Y => divisor .
|
||||
// --------------- .
|
||||
// Z Z Z Z Z Z Z Z => result
|
||||
// For the next iteration we only care about result if divisor is <= remainder
|
||||
// (which implies result <= remainder). Otherwise we care about remainder
|
||||
// (recall re-storing division). Hence we optimise subtraction and
|
||||
// ignore full adders for places where remainder bits are known to be false
|
||||
// bits. We instead use `ANDs` to compute the carry overs, since the
|
||||
// last carry over indicates whether the value has overflown (i.e. divisor <=
|
||||
// remainder). Last carry out is `true` if value has not overflown, otherwise
|
||||
// false.
|
||||
let mut carry =
|
||||
full_adder_plain_carry_in(evaluator, &mut subtract[i], &neg_b[0], true, key);
|
||||
for j in 1..i + 1 {
|
||||
carry = full_adder(evaluator, &mut subtract[i - j], &neg_b[j], &carry, key);
|
||||
}
|
||||
for j in i + 1..n {
|
||||
// All I care about are the carries
|
||||
evaluator.and_inplace(&mut carry, &neg_b[j], key);
|
||||
}
|
||||
|
||||
let not_carry = evaluator.not(&carry);
|
||||
// Choose `remainder` if subtraction has overflown (i.e. carry = false).
|
||||
// Otherwise choose `subtractor`.
|
||||
//
|
||||
// mux k^a | !(k)^b, where k is the selector.
|
||||
izip!(remainder.iter_mut(), subtract.iter_mut()).for_each(|(r, s)| {
|
||||
// choose `s` when carry is true, otherwise choose r
|
||||
evaluator.and_inplace(s, &carry, key);
|
||||
evaluator.and_inplace(r, ¬_carry, key);
|
||||
evaluator.or_inplace(r, s, key);
|
||||
});
|
||||
|
||||
// Set i^th MSB of quotient to 1 if carry = true, otherwise set it to 0.
|
||||
// X&1 | X&0 => X&1 => X
|
||||
quotient.push(carry);
|
||||
}
|
||||
|
||||
remainder.reverse();
|
||||
quotient.reverse();
|
||||
|
||||
(quotient, remainder)
|
||||
}
|
||||
|
||||
fn is_zero<E: BooleanGates>(evaluator: &mut E, a: &[E::Ciphertext], key: &E::Key) -> E::Ciphertext {
|
||||
let mut a = a.iter().map(|v| evaluator.not(v)).collect_vec();
|
||||
let (out, rest_a) = a.split_at_mut(1);
|
||||
rest_a.iter().for_each(|c| {
|
||||
evaluator.and_inplace(&mut out[0], c, key);
|
||||
});
|
||||
return a.remove(0);
|
||||
}
|
||||
|
||||
fn arbitrary_bit_equality<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
assert!(a.len() == b.len());
|
||||
let mut out = evaluator.and(&a[0], &b[0], key);
|
||||
izip!(a.iter(), b.iter()).skip(1).for_each(|(abit, bbit)| {
|
||||
let e = evaluator.xnor(abit, bbit, key);
|
||||
evaluator.and(&mut out, &e, key);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Comaprator handle computes comparator result 2ns MSB onwards. It is
|
||||
/// separated because comparator subroutine for signed and unsgind integers
|
||||
/// differs only for 1st MSB and is common second MSB onwards
|
||||
fn _comparator_handler_from_second_msb<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
mut comp: E::Ciphertext,
|
||||
mut casc: E::Ciphertext,
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
let n = a.len();
|
||||
|
||||
// handle MSB - 1
|
||||
let mut tmp = evaluator.not(&b[n - 2]);
|
||||
evaluator.and(&mut tmp, &a[n - 2], key);
|
||||
evaluator.and(&mut tmp, &casc, key);
|
||||
evaluator.or(&mut comp, &tmp, key);
|
||||
|
||||
for i in 2..n {
|
||||
// calculate cascading bit
|
||||
let tmp_casc = evaluator.xnor(&a[n - 2 - i], &b[n - 2 - i], key);
|
||||
evaluator.and(&mut casc, &tmp_casc, key);
|
||||
|
||||
// calculate computate bit
|
||||
let mut tmp = evaluator.not(&b[n - 1 - i]);
|
||||
evaluator.and(&mut tmp, &a[n - 1 - i], key);
|
||||
evaluator.and(&mut tmp, &casc, key);
|
||||
evaluator.or(&mut comp, &tmp, key);
|
||||
}
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
/// Signed integer comparison is same as unsigned integer with MSB flipped.
|
||||
fn arbitrary_signed_bit_comparator<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
assert!(a.len() == b.len());
|
||||
let n = a.len();
|
||||
|
||||
// handle MSB
|
||||
let mut comp = evaluator.not(&a[n - 1]);
|
||||
evaluator.and(&mut comp, &b[n - 1], key); // comp
|
||||
let casc = evaluator.xnor(&a[n - 1], &b[n - 1], key); // casc
|
||||
|
||||
return _comparator_handler_from_second_msb(evaluator, a, b, comp, casc, key);
|
||||
}
|
||||
|
||||
fn arbitrary_bit_comparator<E: BooleanGates>(
|
||||
evaluator: &mut E,
|
||||
a: &[E::Ciphertext],
|
||||
b: &[E::Ciphertext],
|
||||
key: &E::Key,
|
||||
) -> E::Ciphertext {
|
||||
assert!(a.len() == b.len());
|
||||
let n = a.len();
|
||||
|
||||
// handle MSB
|
||||
let mut comp = evaluator.not(&b[n - 1]);
|
||||
evaluator.and(&mut comp, &a[n - 1], key);
|
||||
let casc = evaluator.xnor(&a[n - 1], &b[n - 1], key);
|
||||
|
||||
return _comparator_handler_from_second_msb(evaluator, a, b, comp, casc, key);
|
||||
}
|
||||
14
src/shortint/types.rs
Normal file
14
src/shortint/types.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
#[derive(Clone)]
|
||||
pub(super) struct FheUint8<C> {
|
||||
pub(super) data: Vec<C>,
|
||||
}
|
||||
|
||||
impl<C> FheUint8<C> {
|
||||
pub(super) fn data(&self) -> &[C] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub(super) fn data_mut(&mut self) -> &mut [C] {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user