concrete_integer/server_key/radix/sub.rs
1use crate::ciphertext::RadixCiphertext;
2use crate::server_key::CheckError;
3use crate::server_key::CheckError::CarryFull;
4use crate::ServerKey;
5
6impl ServerKey {
7 /// Computes homomorphically a subtraction between two ciphertexts encrypting integer values.
8 ///
9 /// This function computes the subtraction without checking if it exceeds the capacity of the
10 /// ciphertext.
11 ///
12 /// The result is returned as a new ciphertext.
13 ///
14 /// # Example
15 ///
16 /// ```rust
17 /// use concrete_integer::gen_keys_radix;
18 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
19 ///
20 /// let num_blocks = 4;
21 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
22 ///
23 /// let msg_1 = 12;
24 /// let msg_2 = 10;
25 ///
26 /// // Encrypt two messages:
27 /// let ctxt_1 = cks.encrypt(msg_1);
28 /// let ctxt_2 = cks.encrypt(msg_2);
29 ///
30 /// // Compute homomorphically a subtraction:
31 /// let ct_res = sks.unchecked_sub(&ctxt_1, &ctxt_2);
32 ///
33 /// // Decrypt:
34 /// let dec_result = cks.decrypt(&ct_res);
35 /// assert_eq!(dec_result, msg_1 - msg_2);
36 /// ```
37 pub fn unchecked_sub(
38 &self,
39 ctxt_left: &RadixCiphertext,
40 ctxt_right: &RadixCiphertext,
41 ) -> RadixCiphertext {
42 let mut result = ctxt_left.clone();
43 self.unchecked_sub_assign(&mut result, ctxt_right);
44 result
45 }
46
47 /// Computes homomorphically a subtraction between two ciphertexts encrypting integer values.
48 ///
49 /// This function computes the subtraction without checking if it exceeds the capacity of the
50 /// ciphertext.
51 ///
52 /// The result is assigned to the `ct_left` ciphertext.
53 ///
54 /// # Example
55 ///
56 /// ```rust
57 /// use concrete_integer::gen_keys_radix;
58 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
59 ///
60 /// // We have 4 * 2 = 8 bits of message
61 /// let num_blocks = 4;
62 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
63 ///
64 /// let msg_1 = 128;
65 /// let msg_2 = 99;
66 ///
67 /// // Encrypt two messages:
68 /// let mut ctxt_1 = cks.encrypt(msg_1);
69 /// let ctxt_2 = cks.encrypt(msg_2);
70 ///
71 /// // Compute homomorphically a subtraction:
72 /// sks.unchecked_sub_assign(&mut ctxt_1, &ctxt_2);
73 ///
74 /// // Decrypt:
75 /// let dec_result = cks.decrypt(&ctxt_1);
76 /// assert_eq!(dec_result, msg_1 - msg_2);
77 /// ```
78 pub fn unchecked_sub_assign(
79 &self,
80 ctxt_left: &mut RadixCiphertext,
81 ctxt_right: &RadixCiphertext,
82 ) {
83 let neg = self.unchecked_neg(ctxt_right);
84 self.unchecked_add_assign(ctxt_left, &neg);
85 }
86
87 /// Verifies if ct_right can be subtracted to ct_left.
88 ///
89 /// # Example
90 ///
91 ///```rust
92 /// use concrete_integer::gen_keys_radix;
93 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
94 ///
95 /// // We have 4 * 2 = 8 bits of message
96 /// let num_blocks = 4;
97 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
98 ///
99 /// let msg_1 = 182;
100 /// let msg_2 = 120;
101 ///
102 /// // Encrypt two messages:
103 /// let ctxt_1 = cks.encrypt(msg_1);
104 /// let ctxt_2 = cks.encrypt(msg_2);
105 ///
106 /// // Check if we can perform a subtraction
107 /// let res = sks.is_sub_possible(&ctxt_1, &ctxt_2);
108 ///
109 /// assert_eq!(true, res);
110 /// ```
111 pub fn is_sub_possible(
112 &self,
113 ctxt_left: &RadixCiphertext,
114 ctxt_right: &RadixCiphertext,
115 ) -> bool {
116 for (ct_left_i, ct_right_i) in ctxt_left.blocks.iter().zip(ctxt_right.blocks.iter()) {
117 if !self.key.is_sub_possible(ct_left_i, ct_right_i) {
118 return false;
119 }
120 }
121 true
122 }
123
124 /// Computes homomorphically a subtraction between two ciphertexts encrypting integer values.
125 ///
126 /// If the operation can be performed, the result is returned in a new ciphertext.
127 /// Otherwise [CheckError::CarryFull] is returned.
128 ///
129 /// The result is returned as a new ciphertext.
130 ///
131 /// # Example
132 ///
133 /// ```rust
134 /// use concrete_integer::gen_keys_radix;
135 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
136 ///
137 /// // We have 4 * 2 = 8 bits of message
138 /// let num_blocks = 4;
139 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
140 ///
141 /// let msg = 1;
142 ///
143 /// // Encrypt two messages:
144 /// let ctxt_1 = cks.encrypt(msg);
145 /// let ctxt_2 = cks.encrypt(msg);
146 ///
147 /// // Compute homomorphically a subtraction:
148 /// let ct_res = sks.checked_sub(&ctxt_1, &ctxt_2);
149 ///
150 /// match ct_res {
151 /// Err(x) => panic!("{:?}", x),
152 /// Ok(y) => {
153 /// let clear = cks.decrypt(&y);
154 /// assert_eq!(0, clear);
155 /// }
156 /// }
157 /// ```
158 pub fn checked_sub(
159 &self,
160 ctxt_left: &RadixCiphertext,
161 ctxt_right: &RadixCiphertext,
162 ) -> Result<RadixCiphertext, CheckError> {
163 if self.is_sub_possible(ctxt_left, ctxt_right) {
164 Ok(self.unchecked_sub(ctxt_left, ctxt_right))
165 } else {
166 Err(CarryFull)
167 }
168 }
169
170 /// Computes homomorphically a subtraction between two ciphertexts encrypting integer values.
171 ///
172 /// If the operation can be performed, the result is returned in a new ciphertext.
173 /// Otherwise [CheckError::CarryFull] is returned.
174 ///
175 /// The result is assigned to the `ct_left` ciphertext.
176 ///
177 /// # Example
178 ///
179 /// ```rust
180 /// use concrete_integer::gen_keys_radix;
181 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
182 ///
183 /// let num_blocks = 4;
184 ///
185 /// // Generate the client key and the server key:
186 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
187 ///
188 /// let msg1 = 41u8;
189 /// let msg2 = 101u8;
190 ///
191 /// let mut ct1 = cks.encrypt(msg1 as u64);
192 /// let ct2 = cks.encrypt(msg2 as u64);
193 ///
194 /// // Compute homomorphically an addition:
195 /// let res = sks.checked_sub_assign(&mut ct1, &ct2);
196 ///
197 /// assert!(res.is_ok());
198 ///
199 /// let clear = cks.decrypt(&ct1);
200 /// assert_eq!(msg1.wrapping_sub(msg2) as u64, clear);
201 /// ```
202 pub fn checked_sub_assign(
203 &self,
204 ct_left: &mut RadixCiphertext,
205 ct_right: &RadixCiphertext,
206 ) -> Result<(), CheckError> {
207 if self.is_sub_possible(ct_left, ct_right) {
208 self.unchecked_sub_assign(ct_left, ct_right);
209 Ok(())
210 } else {
211 Err(CarryFull)
212 }
213 }
214
215 /// Computes homomorphically the subtraction between ct_left and ct_right.
216 ///
217 /// # Example
218 ///
219 /// ```rust
220 /// use concrete_integer::gen_keys_radix;
221 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
222 ///
223 /// // We have 4 * 2 = 8 bits of message
224 /// let num_blocks = 4;
225 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
226 ///
227 /// let msg_1 = 120u8;
228 /// let msg_2 = 181u8;
229 ///
230 /// // Encrypt two messages:
231 /// let mut ctxt_1 = cks.encrypt(msg_1 as u64);
232 /// let mut ctxt_2 = cks.encrypt(msg_2 as u64);
233 ///
234 /// // Compute homomorphically a subtraction
235 /// let ct_res = sks.smart_sub(&mut ctxt_1, &mut ctxt_2);
236 ///
237 /// // Decrypt:
238 /// let res = cks.decrypt(&ct_res);
239 /// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
240 /// ```
241 pub fn smart_sub(
242 &self,
243 ctxt_left: &mut RadixCiphertext,
244 ctxt_right: &mut RadixCiphertext,
245 ) -> RadixCiphertext {
246 // If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
247 if !self.is_neg_possible(ctxt_right) {
248 self.full_propagate(ctxt_right);
249 }
250
251 // If the ciphertext cannot be added together without exceeding the capacity of a ciphertext
252 if !self.is_sub_possible(ctxt_left, ctxt_right) {
253 self.full_propagate(ctxt_left);
254 self.full_propagate(ctxt_right);
255 }
256
257 let mut result = ctxt_left.clone();
258 self.unchecked_sub_assign(&mut result, ctxt_right);
259
260 result
261 }
262
263 /// Computes homomorphically the subtraction between ct_left and ct_right.
264 ///
265 /// # Example
266 ///
267 /// ```rust
268 /// use concrete_integer::gen_keys_radix;
269 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
270 ///
271 /// // We have 4 * 2 = 8 bits of message
272 /// let num_blocks = 4;
273 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
274 ///
275 /// let msg_1 = 120u8;
276 /// let msg_2 = 181u8;
277 ///
278 /// // Encrypt two messages:
279 /// let mut ctxt_1 = cks.encrypt(msg_1 as u64);
280 /// let mut ctxt_2 = cks.encrypt(msg_2 as u64);
281 ///
282 /// // Compute homomorphically a subtraction
283 /// sks.smart_sub_assign(&mut ctxt_1, &mut ctxt_2);
284 ///
285 /// // Decrypt:
286 /// let res = cks.decrypt(&ctxt_1);
287 /// assert_eq!(msg_1.wrapping_sub(msg_2) as u64, res);
288 /// ```
289 pub fn smart_sub_assign(
290 &self,
291 ctxt_left: &mut RadixCiphertext,
292 ctxt_right: &mut RadixCiphertext,
293 ) {
294 // If the ciphertext cannot be negated without exceeding the capacity of a ciphertext
295 if !self.is_neg_possible(ctxt_right) {
296 self.full_propagate(ctxt_right);
297 }
298
299 // If the ciphertext cannot be added together without exceeding the capacity of a ciphertext
300 if !self.is_sub_possible(ctxt_left, ctxt_right) {
301 self.full_propagate(ctxt_left);
302 self.full_propagate(ctxt_right);
303 }
304
305 self.unchecked_sub_assign(ctxt_left, ctxt_right);
306 }
307}