use crate::core_crypto::prelude::{CastFrom, CastInto, Numeric, SignedNumeric, UnsignedInteger};
use crate::integer::bigint::{StaticUnsignedBigInt, I1024, I2048, I4096, U1024, U2048, U4096};
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::ciphertext::{RadixCiphertext, SignedRadixCiphertext};
use crate::integer::server_key::radix::scalar_mul::ScalarMultiplier;
use crate::integer::{IntegerCiphertext, ServerKey, I256, I512, U256, U512};
use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Neg, Shl, Shr, Sub};
#[inline(always)]
pub(crate) fn is_even<T>(d: T) -> bool
where
T: Numeric + BitAnd<T, Output = T>,
{
(d & T::ONE) == T::ZERO
}
pub trait MiniUnsignedInteger:
Numeric
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
+ Add<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ Sub<Self, Output = Self>
+ BitAnd<Self, Output = Self>
+ AddAssign<Self>
{
fn ceil_ilog2(self) -> u32;
fn ilog2(self) -> u32;
fn is_power_of_two(self) -> bool;
fn wrapping_sub(self, other: Self) -> Self;
}
impl<T> MiniUnsignedInteger for T
where
T: UnsignedInteger,
{
fn ceil_ilog2(self) -> u32 {
<T as UnsignedInteger>::ceil_ilog2(self)
}
fn ilog2(self) -> u32 {
<T as UnsignedInteger>::ilog2(self)
}
fn is_power_of_two(self) -> bool {
<T as UnsignedInteger>::is_power_of_two(self)
}
fn wrapping_sub(self, other: Self) -> Self {
<T as UnsignedInteger>::wrapping_sub(self, other)
}
}
impl<const N: usize> MiniUnsignedInteger for StaticUnsignedBigInt<N> {
fn ceil_ilog2(self) -> u32 {
self.ceil_ilog2()
}
fn ilog2(self) -> u32 {
self.ilog2()
}
fn is_power_of_two(self) -> bool {
self.is_power_of_two()
}
fn wrapping_sub(self, other: Self) -> Self {
self.wrapping_sub(other)
}
}
pub trait Reciprocable: MiniUnsignedInteger {
type DoublePrecision: MiniUnsignedInteger
+ CastFrom<Self>
+ CastInto<Self>
+ CastInto<u64>
+ ScalarMultiplier + DecomposableInto<u8>; }
impl Reciprocable for u8 {
type DoublePrecision = u16;
}
impl Reciprocable for u16 {
type DoublePrecision = u32;
}
impl Reciprocable for u32 {
type DoublePrecision = u64;
}
impl Reciprocable for u64 {
type DoublePrecision = u128;
}
impl Reciprocable for u128 {
type DoublePrecision = U256;
}
impl Reciprocable for U256 {
type DoublePrecision = U512;
}
impl Reciprocable for U512 {
type DoublePrecision = U1024;
}
impl Reciprocable for U1024 {
type DoublePrecision = U2048;
}
impl Reciprocable for U2048 {
type DoublePrecision = U4096;
}
pub trait SignedReciprocable:
DecomposableInto<u64>
+ DecomposableInto<u8>
+ SignedNumeric
+ Neg<Output = Self>
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
+ std::fmt::Debug
{
type Unsigned: Reciprocable + CastFrom<Self> + std::fmt::Debug;
type DoublePrecision: DecomposableInto<u8>
+ ScalarMultiplier
+ CastFrom<<Self::Unsigned as Reciprocable>::DoublePrecision>
+ CastInto<u64>
+ std::fmt::Debug;
fn wrapping_abs(self) -> Self;
}
impl SignedReciprocable for i8 {
type Unsigned = u8;
type DoublePrecision = i16;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for i16 {
type Unsigned = u16;
type DoublePrecision = i32;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for i32 {
type Unsigned = u32;
type DoublePrecision = i64;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for i64 {
type Unsigned = u64;
type DoublePrecision = i128;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for i128 {
type Unsigned = u128;
type DoublePrecision = I256;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for I256 {
type Unsigned = U256;
type DoublePrecision = I512;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for I512 {
type Unsigned = U512;
type DoublePrecision = I1024;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for I1024 {
type Unsigned = U1024;
type DoublePrecision = I2048;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
impl SignedReciprocable for I2048 {
type Unsigned = U2048;
type DoublePrecision = I4096;
fn wrapping_abs(self) -> Self {
self.wrapping_abs()
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct ApproximatedMultiplier<T> {
pub(crate) multiplier: T,
pub(crate) shift_post: u32,
pub(crate) l: u32,
}
#[allow(non_snake_case)]
pub(crate) fn choose_multiplier<T: Reciprocable>(
divisor: T,
precision: u32,
integer_bits: u32,
) -> ApproximatedMultiplier<T::DoublePrecision> {
let N = integer_bits;
assert_ne!(divisor, T::ZERO, "attempt to divide by 0");
assert!(precision >= 1 && precision <= N);
let d = T::DoublePrecision::cast_from(divisor);
let two_pow_n = T::DoublePrecision::ONE << N;
let l = d.ceil_ilog2();
let mut shift_post = l;
let mut m_low = (two_pow_n * ((T::DoublePrecision::ONE << l as usize) - d)) / d;
m_low += two_pow_n;
let mut m_high = ((two_pow_n * ((T::DoublePrecision::ONE << l as usize) - d))
+ (T::DoublePrecision::ONE << (N + l - precision)))
/ d;
m_high += two_pow_n;
assert!(m_low < m_high);
loop {
let m_low_i = m_low >> 1usize;
let m_high_i = m_high >> 1usize;
if m_low_i >= m_high_i || shift_post == 0 {
break;
}
m_low = m_low_i;
m_high = m_high_i;
shift_post -= 1;
}
ApproximatedMultiplier {
multiplier: m_high,
shift_post,
l,
}
}
impl ServerKey {
fn scalar_mul_high<T>(&self, lhs: &RadixCiphertext, rhs: T) -> RadixCiphertext
where
T: ScalarMultiplier + DecomposableInto<u8>,
{
let mut result = lhs.clone();
self.extend_radix_with_trivial_zero_blocks_msb_assign(&mut result, lhs.blocks.len());
self.scalar_mul_assign_parallelized(&mut result, rhs);
self.trim_radix_blocks_lsb_assign(&mut result, lhs.blocks.len());
result
}
fn signed_scalar_mul_high<T>(
&self,
lhs: &SignedRadixCiphertext,
rhs: T,
) -> SignedRadixCiphertext
where
T: ScalarMultiplier + DecomposableInto<u8>,
{
let num_blocks = lhs.blocks.len();
let mut result = self.extend_radix_with_sign_msb(lhs, num_blocks);
self.scalar_mul_assign_parallelized(&mut result, rhs);
let mut result = RadixCiphertext::from_blocks(result.blocks);
self.trim_radix_blocks_lsb_assign(&mut result, num_blocks);
SignedRadixCiphertext::from_blocks(result.blocks)
}
pub fn unchecked_scalar_div_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable,
{
let numerator_bits = self.key.message_modulus.0.ilog2() * numerator.blocks.len() as u32;
assert_ne!(divisor, T::ZERO, "attempt to divide by 0");
assert!(
T::BITS >= numerator_bits as usize,
"The scalar divisor type must have a number of bits that is \
>= to the number of bits encrypted in the ciphertext: \n\
encrypted bits: {numerator_bits}, scalar bits: {}
",
T::BITS
);
if divisor.is_power_of_two() {
return self
.unchecked_scalar_right_shift_parallelized(numerator, divisor.ilog2() as u64);
}
let log2_divisor = divisor.ceil_ilog2();
if log2_divisor > numerator_bits {
return self.create_trivial_zero_radix(numerator.blocks.len());
}
let mut chosen_multiplier = choose_multiplier(divisor, numerator_bits, numerator_bits);
let shift_pre = if chosen_multiplier.multiplier
>= (T::DoublePrecision::ONE << numerator_bits as usize)
&& is_even(divisor)
{
let divisor = T::DoublePrecision::cast_from(divisor);
let two_pow_e =
divisor & ((T::DoublePrecision::ONE << numerator_bits as usize) - divisor);
let e = MiniUnsignedInteger::ilog2(two_pow_e);
let divisor_odd = divisor / two_pow_e;
assert!(numerator_bits > e && e <= T::BITS as u32);
let divisor_odd: T = divisor_odd.cast_into(); chosen_multiplier = choose_multiplier(divisor_odd, numerator_bits - e, numerator_bits);
e as u64
} else {
0
};
if chosen_multiplier.multiplier >= (T::DoublePrecision::ONE << numerator_bits as usize) {
assert!(shift_pre == 0);
let inverse =
chosen_multiplier.multiplier - (T::DoublePrecision::ONE << numerator_bits as usize);
let t1 = self.scalar_mul_high(numerator, inverse);
assert_eq!(t1.blocks.len(), numerator.blocks.len());
let mut quotient = self.sub_parallelized(numerator, &t1);
self.unchecked_scalar_right_shift_assign_parallelized(&mut quotient, 1);
self.add_assign_parallelized(&mut quotient, &t1);
assert!(chosen_multiplier.shift_post > 0);
self.unchecked_scalar_right_shift_assign_parallelized(
&mut quotient,
chosen_multiplier.shift_post as u64 - 1,
);
quotient
} else {
let shifted_n = self.unchecked_scalar_right_shift(numerator, shift_pre);
let mut quotient = self.scalar_mul_high(&shifted_n, chosen_multiplier.multiplier);
self.unchecked_scalar_right_shift_assign_parallelized(
&mut quotient,
chosen_multiplier.shift_post as u64,
);
quotient
}
}
pub fn unchecked_signed_scalar_div_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> SignedRadixCiphertext
where
T: SignedReciprocable,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
assert_ne!(divisor, T::ZERO, "attempt to divide by 0");
let numerator_bits = self.key.message_modulus.0.ilog2() * numerator.blocks.len() as u32;
assert!(
T::BITS >= numerator_bits as usize,
"The scalar divisor type must have a number of bits that is\
>= to the number of bits encrypted in the ciphertext"
);
let absolute_divisor = T::Unsigned::cast_from(divisor.wrapping_abs());
if absolute_divisor == T::Unsigned::ONE {
return if divisor < T::ZERO {
self.neg_parallelized(numerator)
} else {
numerator.clone()
};
}
let chosen_multiplier =
choose_multiplier(absolute_divisor, numerator_bits - 1, numerator_bits);
if chosen_multiplier.l >= numerator_bits {
return self.create_trivial_zero_radix(numerator.blocks.len());
}
let quotient;
if absolute_divisor == (T::Unsigned::ONE << chosen_multiplier.l as usize) {
let l = chosen_multiplier.l;
let mut tmp = self.unchecked_scalar_right_shift_parallelized(numerator, l - 1);
self.unchecked_scalar_right_shift_logical_assign_parallelized(
&mut tmp,
(numerator_bits - l) as usize,
);
self.add_assign_parallelized(&mut tmp, numerator);
self.unchecked_scalar_right_shift_assign_parallelized(&mut tmp, l);
quotient = tmp;
} else if chosen_multiplier.multiplier
< (<T::Unsigned as Reciprocable>::DoublePrecision::ONE << (numerator_bits - 1))
{
let (mut tmp, xsign) = rayon::join(
move || {
let mut tmp =
self.signed_scalar_mul_high(numerator, chosen_multiplier.multiplier);
self.unchecked_scalar_right_shift_arithmetic_assign_parallelized(
&mut tmp,
chosen_multiplier.shift_post,
);
tmp
},
|| {
self.unchecked_scalar_right_shift_arithmetic_parallelized(
numerator,
numerator_bits - 1,
)
},
);
self.sub_assign_parallelized(&mut tmp, &xsign);
quotient = tmp;
} else {
let (mut tmp, xsign) = rayon::join(
move || {
let cst = chosen_multiplier.multiplier.wrapping_sub(
<T::Unsigned as Reciprocable>::DoublePrecision::ONE << numerator_bits,
);
let cst = T::DoublePrecision::cast_from(cst);
let mut tmp = self.signed_scalar_mul_high(numerator, cst);
self.add_assign_parallelized(&mut tmp, numerator);
self.unchecked_scalar_right_shift_assign_parallelized(
&mut tmp,
chosen_multiplier.shift_post,
);
tmp
},
|| {
self.unchecked_scalar_right_shift_parallelized(numerator, numerator_bits - 1)
},
);
self.sub_assign_parallelized(&mut tmp, &xsign);
quotient = tmp;
}
if divisor < T::ZERO {
self.neg_parallelized("ient)
} else {
quotient
}
}
pub fn unchecked_signed_scalar_div_rem_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> (SignedRadixCiphertext, SignedRadixCiphertext)
where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
let quotient = self.unchecked_signed_scalar_div_parallelized(numerator, divisor);
let tmp = self.unchecked_scalar_mul_parallelized("ient, divisor);
let remainder = self.sub_parallelized(numerator, &tmp);
(quotient, remainder)
}
pub fn unchecked_signed_scalar_rem_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> SignedRadixCiphertext
where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
let (_, remainder) = self.unchecked_signed_scalar_div_rem_parallelized(numerator, divisor);
remainder
}
pub fn signed_scalar_div_rem_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> (SignedRadixCiphertext, SignedRadixCiphertext)
where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
if numerator.block_carries_are_empty() {
self.unchecked_signed_scalar_div_rem_parallelized(numerator, divisor)
} else {
let mut tmp = numerator.clone();
self.full_propagate_parallelized(&mut tmp);
self.unchecked_signed_scalar_div_rem_parallelized(&tmp, divisor)
}
}
pub fn signed_scalar_div_assign_parallelized<T>(
&self,
numerator: &mut SignedRadixCiphertext,
divisor: T,
) where
T: SignedReciprocable,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
*numerator = self.unchecked_signed_scalar_div_parallelized(numerator, divisor);
}
pub fn signed_scalar_div_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> SignedRadixCiphertext
where
T: SignedReciprocable,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
let mut result = numerator.clone();
self.signed_scalar_div_assign_parallelized(&mut result, divisor);
result
}
pub fn signed_scalar_rem_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> SignedRadixCiphertext
where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
let mut result = numerator.clone();
self.signed_scalar_rem_assign_parallelized(&mut result, divisor);
result
}
pub fn signed_scalar_rem_assign_parallelized<T>(
&self,
numerator: &mut SignedRadixCiphertext,
divisor: T,
) where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
let remainder = self.unchecked_signed_scalar_rem_parallelized(numerator, divisor);
*numerator = remainder;
}
pub fn unchecked_signed_scalar_div_floor_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> SignedRadixCiphertext
where
T: SignedReciprocable,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
assert_ne!(divisor, T::ZERO, "Cannot divide by zero");
let numerator_bits = self.key.message_modulus.0.ilog2() * numerator.blocks.len() as u32;
if divisor < T::ZERO {
let dsign = divisor >> (T::BITS - 1);
let mut nsign = self.scalar_add_parallelized(numerator, dsign);
self.unchecked_bitor_assign_parallelized(&mut nsign, numerator);
self.scalar_right_shift_assign_parallelized(&mut nsign, numerator_bits - 1);
let qsign = self.unchecked_scalar_bitxor_parallelized(&nsign, dsign);
let mut new_n = numerator.clone();
self.smart_scalar_add_assign_parallelized(&mut new_n, dsign);
self.smart_sub_assign_parallelized(&mut new_n, &mut nsign);
self.full_propagate_parallelized(&mut new_n);
let mut q = self.unchecked_signed_scalar_div_parallelized(&new_n, divisor);
self.add_assign_parallelized(&mut q, &qsign);
q
} else {
let chosen_multiplier = choose_multiplier(
T::Unsigned::cast_from(divisor),
numerator_bits - 1,
numerator_bits,
);
if chosen_multiplier.l >= numerator_bits {
let divisor_sign_bit_is_set = u64::from(divisor < T::ZERO);
let sign_bit_pos = self.key.message_modulus.0.ilog2() - 1;
let lut = self.key.generate_lookup_table(|x| {
let x = x % self.key.message_modulus.0;
let numerator_sign_bit_is_set = (x >> sign_bit_pos) & 1;
let numerator_and_divisor_sign_differs =
numerator_sign_bit_is_set != divisor_sign_bit_is_set;
if numerator_and_divisor_sign_differs {
self.key.message_modulus.0 - 1
} else {
0
}
});
let block = self
.key
.apply_lookup_table(&numerator.blocks[numerator.blocks.len() - 1], &lut);
let blocks = vec![block; numerator.blocks.len()];
return SignedRadixCiphertext::from(blocks);
}
assert!(chosen_multiplier.l <= (T::BITS - 1) as u32);
if divisor == (T::ONE << chosen_multiplier.l) {
self.unchecked_scalar_right_shift_parallelized(numerator, chosen_multiplier.l)
} else {
assert!(
chosen_multiplier.multiplier
< <T::Unsigned as Reciprocable>::DoublePrecision::ONE << numerator_bits
);
let xsign =
self.unchecked_scalar_right_shift_parallelized(numerator, numerator_bits - 1);
let tmp = self.unchecked_bitxor_parallelized(&xsign, numerator);
let tmp = RadixCiphertext::from(tmp.blocks);
let mut tmp = self.scalar_mul_high(&tmp, chosen_multiplier.multiplier);
self.unchecked_scalar_right_shift_logical_assign_parallelized(
&mut tmp,
chosen_multiplier.shift_post,
);
let xsign = RadixCiphertext::from(xsign.blocks);
self.unchecked_bitxor_assign_parallelized(&mut tmp, &xsign);
SignedRadixCiphertext::from(tmp.blocks)
}
}
}
pub fn unchecked_signed_scalar_div_rem_floor_parallelized<T>(
&self,
numerator: &SignedRadixCiphertext,
divisor: T,
) -> (SignedRadixCiphertext, SignedRadixCiphertext)
where
T: SignedReciprocable + ScalarMultiplier,
<<T as SignedReciprocable>::Unsigned as Reciprocable>::DoublePrecision: Send,
{
let quotient = self.unchecked_signed_scalar_div_floor_parallelized(numerator, divisor);
let tmp = self.unchecked_scalar_mul_parallelized("ient, divisor);
let remainder = self.sub_parallelized(numerator, &tmp);
(quotient, remainder)
}
pub fn unchecked_scalar_div_rem_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> (RadixCiphertext, RadixCiphertext)
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
let quotient = self.unchecked_scalar_div_parallelized(numerator, divisor);
let remainder = if MiniUnsignedInteger::is_power_of_two(divisor) {
self.scalar_bitand_parallelized(numerator, divisor - T::ONE)
} else {
let tmp = self.unchecked_scalar_mul_parallelized("ient, divisor);
self.sub_parallelized(numerator, &tmp)
};
(quotient, remainder)
}
pub fn unchecked_scalar_rem_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if MiniUnsignedInteger::is_power_of_two(divisor) {
return self.scalar_bitand_parallelized(numerator, divisor - T::ONE);
}
let (_quotient, remainder) = self.unchecked_scalar_div_rem_parallelized(numerator, divisor);
remainder
}
pub fn smart_scalar_div_assign_parallelized<T>(
&self,
numerator: &mut RadixCiphertext,
divisor: T,
) where
T: Reciprocable + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
*numerator = self.unchecked_scalar_div_parallelized(numerator, divisor);
}
pub fn smart_scalar_rem_assign_parallelized<T>(
&self,
numerator: &mut RadixCiphertext,
divisor: T,
) where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
*numerator = self.unchecked_scalar_rem_parallelized(numerator, divisor);
}
pub fn smart_scalar_div_parallelized<T>(
&self,
numerator: &mut RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
self.unchecked_scalar_div_parallelized(numerator, divisor)
}
pub fn smart_scalar_rem_parallelized<T>(
&self,
numerator: &mut RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
self.unchecked_scalar_rem_parallelized(numerator, divisor)
}
pub fn smart_scalar_div_rem_parallelized<T>(
&self,
numerator: &mut RadixCiphertext,
divisor: T,
) -> (RadixCiphertext, RadixCiphertext)
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
self.unchecked_scalar_div_rem_parallelized(numerator, divisor)
}
pub fn scalar_div_assign_parallelized<T>(&self, numerator: &mut RadixCiphertext, divisor: T)
where
T: Reciprocable + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
*numerator = self.unchecked_scalar_div_parallelized(numerator, divisor);
}
pub fn scalar_rem_assign_parallelized<T>(&self, numerator: &mut RadixCiphertext, divisor: T)
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if !numerator.block_carries_are_empty() {
self.full_propagate_parallelized(numerator);
}
*numerator = self.unchecked_scalar_rem_parallelized(numerator, divisor);
}
pub fn scalar_div_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable + DecomposableInto<u8>,
{
let mut result = numerator.clone();
self.scalar_div_assign_parallelized(&mut result, divisor);
result
}
pub fn scalar_rem_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> RadixCiphertext
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
let mut result = numerator.clone();
self.scalar_rem_assign_parallelized(&mut result, divisor);
result
}
pub fn scalar_div_rem_parallelized<T>(
&self,
numerator: &RadixCiphertext,
divisor: T,
) -> (RadixCiphertext, RadixCiphertext)
where
T: Reciprocable + ScalarMultiplier + DecomposableInto<u8>,
{
if numerator.block_carries_are_empty() {
self.unchecked_scalar_div_rem_parallelized(numerator, divisor)
} else {
let mut cloned_numerator = numerator.clone();
self.full_propagate_parallelized(&mut cloned_numerator);
self.unchecked_scalar_div_rem_parallelized(&cloned_numerator, divisor)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_approximated_multiplier() {
let chosen = choose_multiplier(10u64, 32, 32);
assert_eq!(chosen.shift_post, 3);
assert_eq!(chosen.multiplier, ((1u128 << 34) + 1) / 5);
let chosen = choose_multiplier(7u64, 32, 32);
assert_eq!(chosen.multiplier, ((1u128 << 35) + 3) / 7);
let chosen = choose_multiplier(3u64, 31, 32);
assert_eq!(chosen.multiplier, (1u128 << 32).div_ceil(3));
assert_eq!(chosen.shift_post, 0);
}
}