1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
use crate::integer::ciphertext::RadixCiphertext;
use crate::integer::ServerKey;
use crate::shortint::PBSOrderMarker;
impl ServerKey {
/// Computes homomorphically the subtraction between ct_left and ct_right.
///
/// # Example
///
/// ```rust
/// use tfhe::integer::gen_keys_radix;
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// // We have 4 * 2 = 8 bits of message
/// let size = 4;
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
///
/// let msg_1 = 120u8;
/// let msg_2 = 181u8;
///
/// // Encrypt two messages:
/// let mut ctxt_1 = cks.encrypt(msg_1 as u64);
/// let mut ctxt_2 = cks.encrypt(msg_2 as u64);
///
/// // Compute homomorphically a subtraction
/// let ct_res = sks.smart_sub_parallelized(&mut ctxt_1, &mut ctxt_2);
///
/// // Decrypt:
/// let res: u64 = cks.decrypt(&ct_res);
/// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
/// ```
pub fn smart_sub_parallelized<PBSOrder: PBSOrderMarker>(
&self,
ctxt_left: &mut RadixCiphertext<PBSOrder>,
ctxt_right: &mut RadixCiphertext<PBSOrder>,
) -> RadixCiphertext<PBSOrder> {
// If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
if !self.is_neg_possible(ctxt_right) {
self.full_propagate_parallelized(ctxt_right);
}
// If the ciphertext cannot be added together without exceeding the capacity of a ciphertext
if !self.is_sub_possible(ctxt_left, ctxt_right) {
rayon::join(
|| self.full_propagate_parallelized(ctxt_left),
|| self.full_propagate_parallelized(ctxt_right),
);
}
let mut result = ctxt_left.clone();
self.unchecked_sub_assign(&mut result, ctxt_right);
result
}
/// Computes homomorphically the subtraction between ct_left and ct_right.
///
/// # Example
///
/// ```rust
/// use tfhe::integer::gen_keys_radix;
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// // We have 4 * 2 = 8 bits of message
/// let size = 4;
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
///
/// let msg_1 = 120u8;
/// let msg_2 = 181u8;
///
/// // Encrypt two messages:
/// let mut ctxt_1 = cks.encrypt(msg_1 as u64);
/// let mut ctxt_2 = cks.encrypt(msg_2 as u64);
///
/// // Compute homomorphically a subtraction
/// sks.smart_sub_assign_parallelized(&mut ctxt_1, &mut ctxt_2);
///
/// // Decrypt:
/// let res: u64 = cks.decrypt(&ctxt_1);
/// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
/// ```
pub fn smart_sub_assign_parallelized<PBSOrder: PBSOrderMarker>(
&self,
ctxt_left: &mut RadixCiphertext<PBSOrder>,
ctxt_right: &mut RadixCiphertext<PBSOrder>,
) {
// If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
if !self.is_neg_possible(ctxt_right) {
self.full_propagate_parallelized(ctxt_right);
}
// If the ciphertext cannot be added together without exceeding the capacity of a ciphertext
if !self.is_sub_possible(ctxt_left, ctxt_right) {
rayon::join(
|| self.full_propagate_parallelized(ctxt_left),
|| self.full_propagate_parallelized(ctxt_right),
);
}
self.unchecked_sub_assign(ctxt_left, ctxt_right);
}
/// Computes homomorphically the subtraction between ct_left and ct_right.
///
/// This function, like all "default" operations (i.e. not smart, checked or unchecked), will
/// check that the input ciphertexts block carries are empty and clears them if it's not the
/// case and the operation requires it. It outputs a ciphertext whose block carries are always
/// empty.
///
/// 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.
///
/// # Example
///
/// ```rust
/// use tfhe::integer::gen_keys_radix;
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// // We have 4 * 2 = 8 bits of message
/// let size = 4;
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
///
/// let msg_1 = 120u8;
/// let msg_2 = 181u8;
///
/// // Encrypt two messages:
/// let ctxt_1 = cks.encrypt(msg_1 as u64);
/// let ctxt_2 = cks.encrypt(msg_2 as u64);
///
/// // Compute homomorphically a subtraction
/// let ct_res = sks.sub_parallelized(&ctxt_1, &ctxt_2);
///
/// // Decrypt:
/// let res: u64 = cks.decrypt(&ct_res);
/// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
/// ```
pub fn sub_parallelized<PBSOrder: PBSOrderMarker>(
&self,
ctxt_left: &RadixCiphertext<PBSOrder>,
ctxt_right: &RadixCiphertext<PBSOrder>,
) -> RadixCiphertext<PBSOrder> {
let mut ct_res = ctxt_left.clone();
self.sub_assign_parallelized(&mut ct_res, ctxt_right);
ct_res
}
/// Computes homomorphically the subtraction between ct_left and ct_right.
///
/// This function, like all "default" operations (i.e. not smart, checked or unchecked), will
/// check that the input ciphertexts block carries are empty and clears them if it's not the
/// case and the operation requires it. It outputs a ciphertext whose block carries are always
/// empty.
///
/// 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.
///
/// # Example
///
/// ```rust
/// use tfhe::integer::gen_keys_radix;
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// // We have 4 * 2 = 8 bits of message
/// let size = 4;
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
///
/// let msg_1 = 120u8;
/// let msg_2 = 181u8;
///
/// // Encrypt two messages:
/// let mut ctxt_1 = cks.encrypt(msg_1 as u64);
/// let ctxt_2 = cks.encrypt(msg_2 as u64);
///
/// // Compute homomorphically a subtraction
/// sks.sub_assign_parallelized(&mut ctxt_1, &ctxt_2);
///
/// // Decrypt:
/// let res: u64 = cks.decrypt(&ctxt_1);
/// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
/// ```
pub fn sub_assign_parallelized<PBSOrder: PBSOrderMarker>(
&self,
ctxt_left: &mut RadixCiphertext<PBSOrder>,
ctxt_right: &RadixCiphertext<PBSOrder>,
) {
let mut tmp_rhs: RadixCiphertext<PBSOrder>;
let (lhs, rhs) = match (
ctxt_left.block_carries_are_empty(),
ctxt_right.block_carries_are_empty(),
) {
(true, true) => (ctxt_left, ctxt_right),
(true, false) => {
tmp_rhs = ctxt_right.clone();
self.full_propagate_parallelized(&mut tmp_rhs);
(ctxt_left, &tmp_rhs)
}
(false, true) => {
self.full_propagate_parallelized(ctxt_left);
(ctxt_left, ctxt_right)
}
(false, false) => {
tmp_rhs = ctxt_right.clone();
rayon::join(
|| self.full_propagate_parallelized(ctxt_left),
|| self.full_propagate_parallelized(&mut tmp_rhs),
);
(ctxt_left, &tmp_rhs)
}
};
self.unchecked_sub_assign(lhs, rhs);
self.full_propagate_parallelized(lhs);
}
}