concrete_integer/server_key/radix/bitwise_op.rs
1use crate::ciphertext::RadixCiphertext;
2use crate::ServerKey;
3use concrete_shortint::CheckError;
4use concrete_shortint::CheckError::CarryFull;
5
6impl ServerKey {
7 /// Computes homomorphically bitand between two ciphertexts encrypting integer values.
8 ///
9 /// This function computes the operation 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 /// // We have 4 * 2 = 8 bits of message
21 /// let size = 4;
22 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
23 ///
24 /// let msg1 = 201;
25 /// let msg2 = 1;
26 ///
27 /// let ct1 = cks.encrypt(msg1);
28 /// let ct2 = cks.encrypt(msg2);
29 ///
30 /// // Compute homomorphically a bitwise and:
31 /// let ct_res = sks.unchecked_bitand(&ct1, &ct2);
32 ///
33 /// // Decrypt:
34 /// let dec = cks.decrypt(&ct_res);
35 /// assert_eq!(dec, msg1 & msg2);
36 /// ```
37 pub fn unchecked_bitand(
38 &self,
39 ct_left: &RadixCiphertext,
40 ct_right: &RadixCiphertext,
41 ) -> RadixCiphertext {
42 let mut result = ct_left.clone();
43 self.unchecked_bitand_assign(&mut result, ct_right);
44 result
45 }
46
47 pub fn unchecked_bitand_assign(
48 &self,
49 ct_left: &mut RadixCiphertext,
50 ct_right: &RadixCiphertext,
51 ) {
52 for (ct_left_i, ct_right_i) in ct_left.blocks.iter_mut().zip(ct_right.blocks.iter()) {
53 self.key.unchecked_bitand_assign(ct_left_i, ct_right_i);
54 }
55 }
56
57 /// Verifies if a bivariate functional pbs can be applied on ct_left and ct_right.
58 ///
59 /// # Example
60 ///
61 ///```rust
62 /// use concrete_integer::gen_keys_radix;
63 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
64 ///
65 /// let size = 4;
66 ///
67 /// // Generate the client key and the server key:
68 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
69 ///
70 /// let msg1 = 46;
71 /// let msg2 = 87;
72 ///
73 /// let ct1 = cks.encrypt(msg1);
74 /// let ct2 = cks.encrypt(msg2);
75 ///
76 /// let res = sks.is_functional_bivariate_pbs_possible(&ct1, &ct2);
77 ///
78 /// assert_eq!(true, res);
79 /// ```
80 pub fn is_functional_bivariate_pbs_possible(
81 &self,
82 ct_left: &RadixCiphertext,
83 ct_right: &RadixCiphertext,
84 ) -> bool {
85 for (ct_left_i, ct_right_i) in ct_left.blocks.iter().zip(ct_right.blocks.iter()) {
86 if !self
87 .key
88 .is_functional_bivariate_pbs_possible(ct_left_i, ct_right_i)
89 {
90 return false;
91 }
92 }
93 true
94 }
95
96 /// Computes homomorphically a bitand between two ciphertexts encrypting integer values.
97 ///
98 /// If the operation can be performed, the result is returned in a new ciphertext.
99 /// Otherwise [CheckError::CarryFull] is returned.
100 ///
101 /// # Example
102 ///
103 /// ```rust
104 /// use concrete_integer::gen_keys_radix;
105 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
106 ///
107 /// let size = 4;
108 ///
109 /// // Generate the client key and the server key:
110 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
111 ///
112 /// let msg1 = 41;
113 /// let msg2 = 101;
114 ///
115 /// let ct1 = cks.encrypt(msg1);
116 /// let ct2 = cks.encrypt(msg2);
117 ///
118 /// let ct_res = sks.checked_bitand(&ct1, &ct2);
119 ///
120 /// match ct_res {
121 /// Err(x) => panic!("{:?}", x),
122 /// Ok(y) => {
123 /// let clear = cks.decrypt(&y);
124 /// assert_eq!(msg1 & msg2, clear);
125 /// }
126 /// }
127 /// ```
128 pub fn checked_bitand(
129 &self,
130 ct_left: &RadixCiphertext,
131 ct_right: &RadixCiphertext,
132 ) -> Result<RadixCiphertext, CheckError> {
133 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
134 Ok(self.unchecked_bitand(ct_left, ct_right))
135 } else {
136 Err(CarryFull)
137 }
138 }
139
140 /// Computes homomorphically a bitand between two ciphertexts encrypting integer values.
141 ///
142 /// If the operation can be performed, the result is stored in the `ct_left` ciphertext.
143 /// Otherwise [CheckError::CarryFull] is returned, and `ct_left` is not modified.
144 ///
145 /// # Example
146 ///
147 /// ```rust
148 /// use concrete_integer::gen_keys_radix;
149 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
150 ///
151 /// let size = 4;
152 ///
153 /// // Generate the client key and the server key:
154 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
155 ///
156 /// let msg1 = 41;
157 /// let msg2 = 101;
158 ///
159 /// let mut ct1 = cks.encrypt(msg1);
160 /// let ct2 = cks.encrypt(msg2);
161 ///
162 /// let res = sks.checked_bitand_assign(&mut ct1, &ct2);
163 ///
164 /// assert!(res.is_ok());
165 ///
166 /// let clear = cks.decrypt(&ct1);
167 /// assert_eq!(msg1 & msg2, clear);
168 /// ```
169 pub fn checked_bitand_assign(
170 &self,
171 ct_left: &mut RadixCiphertext,
172 ct_right: &RadixCiphertext,
173 ) -> Result<(), CheckError> {
174 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
175 self.unchecked_bitand_assign(ct_left, ct_right);
176 Ok(())
177 } else {
178 Err(CarryFull)
179 }
180 }
181
182 /// Computes homomorphically a bitand between two ciphertexts encrypting integer values.
183 ///
184 /// # Example
185 ///
186 /// ```rust
187 /// use concrete_integer::gen_keys_radix;
188 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
189 ///
190 /// let size = 4;
191 ///
192 /// // Generate the client key and the server key:
193 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
194 ///
195 /// let msg1 = 14;
196 /// let msg2 = 97;
197 ///
198 /// let mut ct1 = cks.encrypt(msg1);
199 /// let mut ct2 = cks.encrypt(msg2);
200 ///
201 /// let ct_res = sks.smart_bitand(&mut ct1, &mut ct2);
202 ///
203 /// // Decrypt:
204 /// let dec_result = cks.decrypt(&ct_res);
205 /// assert_eq!(dec_result, msg1 & msg2);
206 /// ```
207 pub fn smart_bitand(
208 &self,
209 ct_left: &mut RadixCiphertext,
210 ct_right: &mut RadixCiphertext,
211 ) -> RadixCiphertext {
212 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
213 self.full_propagate(ct_left);
214 self.full_propagate(ct_right);
215 }
216 self.unchecked_bitand(ct_left, ct_right)
217 }
218
219 pub fn smart_bitand_assign(
220 &self,
221 ct_left: &mut RadixCiphertext,
222 ct_right: &mut RadixCiphertext,
223 ) {
224 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
225 self.full_propagate(ct_left);
226 self.full_propagate(ct_right);
227 }
228 self.unchecked_bitand_assign(ct_left, ct_right);
229 }
230
231 /// Computes homomorphically bitor between two ciphertexts encrypting integer values.
232 ///
233 /// This function computes the operation without checking if it exceeds the capacity of the
234 /// ciphertext.
235 ///
236 /// The result is returned as a new ciphertext.
237 ///
238 /// # Example
239 ///
240 /// ```rust
241 /// use concrete_integer::gen_keys_radix;
242 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
243 ///
244 /// // We have 4 * 2 = 8 bits of message
245 /// let size = 4;
246 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
247 ///
248 /// let msg1 = 200;
249 /// let msg2 = 1;
250 ///
251 /// let ct1 = cks.encrypt(msg1);
252 /// let ct2 = cks.encrypt(msg2);
253 ///
254 /// // Compute homomorphically a bitwise or:
255 /// let ct_res = sks.unchecked_bitor(&ct1, &ct2);
256 ///
257 /// // Decrypt:
258 /// let dec = cks.decrypt(&ct_res);
259 /// assert_eq!(dec, msg1 | msg2);
260 /// ```
261 pub fn unchecked_bitor(
262 &self,
263 ct_left: &RadixCiphertext,
264 ct_right: &RadixCiphertext,
265 ) -> RadixCiphertext {
266 let mut result = ct_left.clone();
267 self.unchecked_bitor_assign(&mut result, ct_right);
268 result
269 }
270
271 pub fn unchecked_bitor_assign(
272 &self,
273 ct_left: &mut RadixCiphertext,
274 ct_right: &RadixCiphertext,
275 ) {
276 for (ct_left_i, ct_right_i) in ct_left.blocks.iter_mut().zip(ct_right.blocks.iter()) {
277 self.key.unchecked_bitor_assign(ct_left_i, ct_right_i);
278 }
279 }
280
281 /// Computes homomorphically a bitor between two ciphertexts encrypting integer values.
282 ///
283 /// If the operation can be performed, the result is returned in a new ciphertext.
284 /// Otherwise [CheckError::CarryFull] is returned.
285 ///
286 /// # Example
287 ///
288 /// ```rust
289 /// use concrete_integer::gen_keys_radix;
290 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
291 ///
292 /// let size = 4;
293 ///
294 /// // Generate the client key and the server key:
295 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
296 ///
297 /// let msg1 = 41;
298 /// let msg2 = 101;
299 ///
300 /// let ct1 = cks.encrypt(msg1);
301 /// let ct2 = cks.encrypt(msg2);
302 ///
303 /// // Compute homomorphically an addition:
304 /// let ct_res = sks.checked_bitor(&ct1, &ct2);
305 ///
306 /// match ct_res {
307 /// Err(x) => panic!("{:?}", x),
308 /// Ok(y) => {
309 /// let clear = cks.decrypt(&y);
310 /// assert_eq!(msg1 | msg2, clear);
311 /// }
312 /// }
313 /// ```
314 pub fn checked_bitor(
315 &self,
316 ct_left: &RadixCiphertext,
317 ct_right: &RadixCiphertext,
318 ) -> Result<RadixCiphertext, CheckError> {
319 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
320 Ok(self.unchecked_bitor(ct_left, ct_right))
321 } else {
322 Err(CarryFull)
323 }
324 }
325
326 /// Computes homomorphically a bitand between two ciphertexts encrypting integer values.
327 ///
328 /// If the operation can be performed, the result is stored in the `ct_left` ciphertext.
329 /// Otherwise [CheckError::CarryFull] is returned, and `ct_left` is not modified.
330 ///
331 /// # Example
332 ///
333 /// ```rust
334 /// use concrete_integer::gen_keys_radix;
335 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
336 ///
337 /// let size = 4;
338 ///
339 /// // Generate the client key and the server key:
340 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
341 ///
342 /// let msg1 = 41;
343 /// let msg2 = 101;
344 ///
345 /// let mut ct1 = cks.encrypt(msg1);
346 /// let ct2 = cks.encrypt(msg2);
347 ///
348 /// // Compute homomorphically an addition:
349 /// let res = sks.checked_bitor_assign(&mut ct1, &ct2);
350 ///
351 /// assert!(res.is_ok());
352 ///
353 /// let clear = cks.decrypt(&ct1);
354 /// assert_eq!(msg1 | msg2, clear);
355 /// ```
356 pub fn checked_bitor_assign(
357 &self,
358 ct_left: &mut RadixCiphertext,
359 ct_right: &RadixCiphertext,
360 ) -> Result<(), CheckError> {
361 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
362 self.unchecked_bitor_assign(ct_left, ct_right);
363 Ok(())
364 } else {
365 Err(CarryFull)
366 }
367 }
368
369 /// Computes homomorphically a bitor between two ciphertexts encrypting integer values.
370 ///
371 /// # Example
372 ///
373 /// ```rust
374 /// use concrete_integer::gen_keys_radix;
375 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
376 ///
377 /// let size = 4;
378 ///
379 /// // Generate the client key and the server key:
380 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
381 ///
382 /// let msg1 = 14;
383 /// let msg2 = 97;
384 ///
385 /// let mut ct1 = cks.encrypt(msg1);
386 /// let mut ct2 = cks.encrypt(msg2);
387 ///
388 /// let ct_res = sks.smart_bitor(&mut ct1, &mut ct2);
389 ///
390 /// // Decrypt:
391 /// let dec_result = cks.decrypt(&ct_res);
392 /// assert_eq!(dec_result, msg1 | msg2);
393 /// ```
394 pub fn smart_bitor(
395 &self,
396 ct_left: &mut RadixCiphertext,
397 ct_right: &mut RadixCiphertext,
398 ) -> RadixCiphertext {
399 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
400 self.full_propagate(ct_left);
401 self.full_propagate(ct_right);
402 }
403 self.unchecked_bitor(ct_left, ct_right)
404 }
405
406 pub fn smart_bitor_assign(
407 &self,
408 ct_left: &mut RadixCiphertext,
409 ct_right: &mut RadixCiphertext,
410 ) {
411 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
412 self.full_propagate(ct_left);
413 self.full_propagate(ct_right);
414 }
415 self.unchecked_bitor_assign(ct_left, ct_right);
416 }
417
418 /// Computes homomorphically bitxor between two ciphertexts encrypting integer values.
419 ///
420 /// This function computes the operation without checking if it exceeds the capacity of the
421 /// ciphertext.
422 ///
423 /// The result is returned as a new ciphertext.
424 ///
425 /// # Example
426 ///
427 /// ```rust
428 /// use concrete_integer::gen_keys_radix;
429 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
430 ///
431 /// // We have 4 * 2 = 8 bits of message
432 /// let size = 4;
433 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
434 ///
435 /// let msg1 = 49;
436 /// let msg2 = 64;
437 ///
438 /// let ct1 = cks.encrypt(msg1);
439 /// let ct2 = cks.encrypt(msg2);
440 ///
441 /// // Compute homomorphically a bitwise xor:
442 /// let ct_res = sks.unchecked_bitxor(&ct1, &ct2);
443 ///
444 /// // Decrypt:
445 /// let dec = cks.decrypt(&ct_res);
446 /// assert_eq!(msg1 ^ msg2, dec);
447 /// ```
448 pub fn unchecked_bitxor(
449 &self,
450 ct_left: &RadixCiphertext,
451 ct_right: &RadixCiphertext,
452 ) -> RadixCiphertext {
453 let mut result = ct_left.clone();
454 self.unchecked_bitxor_assign(&mut result, ct_right);
455 result
456 }
457
458 pub fn unchecked_bitxor_assign(
459 &self,
460 ct_left: &mut RadixCiphertext,
461 ct_right: &RadixCiphertext,
462 ) {
463 for (ct_left_i, ct_right_i) in ct_left.blocks.iter_mut().zip(ct_right.blocks.iter()) {
464 self.key.unchecked_bitxor_assign(ct_left_i, ct_right_i);
465 }
466 }
467
468 /// Computes homomorphically a bitxor between two ciphertexts encrypting integer values.
469 ///
470 /// If the operation can be performed, the result is returned in a new ciphertext.
471 /// Otherwise [CheckError::CarryFull] is returned.
472 ///
473 /// # Example
474 ///
475 /// ```rust
476 /// use concrete_integer::gen_keys_radix;
477 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
478 ///
479 /// let size = 4;
480 ///
481 /// // Generate the client key and the server key:
482 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
483 ///
484 /// let msg1 = 41;
485 /// let msg2 = 101;
486 ///
487 /// let ct1 = cks.encrypt(msg1);
488 /// let ct2 = cks.encrypt(msg2);
489 ///
490 /// // Compute homomorphically an addition:
491 /// let ct_res = sks.checked_bitxor(&ct1, &ct2);
492 ///
493 /// match ct_res {
494 /// Err(x) => panic!("{:?}", x),
495 /// Ok(y) => {
496 /// let clear = cks.decrypt(&y);
497 /// assert_eq!(msg1 ^ msg2, clear);
498 /// }
499 /// }
500 /// ```
501 pub fn checked_bitxor(
502 &self,
503 ct_left: &RadixCiphertext,
504 ct_right: &RadixCiphertext,
505 ) -> Result<RadixCiphertext, CheckError> {
506 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
507 Ok(self.unchecked_bitxor(ct_left, ct_right))
508 } else {
509 Err(CarryFull)
510 }
511 }
512
513 /// Computes homomorphically a bitxor between two ciphertexts encrypting integer values.
514 ///
515 /// If the operation can be performed, the result is stored in the `ct_left` ciphertext.
516 /// Otherwise [CheckError::CarryFull] is returned, and `ct_left` is not modified.
517 ///
518 /// # Example
519 ///
520 /// ```rust
521 /// use concrete_integer::gen_keys_radix;
522 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
523 ///
524 /// let size = 4;
525 ///
526 /// // Generate the client key and the server key:
527 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
528 ///
529 /// let msg1 = 41;
530 /// let msg2 = 101;
531 ///
532 /// let mut ct1 = cks.encrypt(msg1);
533 /// let ct2 = cks.encrypt(msg2);
534 ///
535 /// // Compute homomorphically an addition:
536 /// let res = sks.checked_bitxor_assign(&mut ct1, &ct2);
537 ///
538 /// assert!(res.is_ok());
539 ///
540 /// let clear = cks.decrypt(&ct1);
541 /// assert_eq!(msg1 ^ msg2, clear);
542 /// ```
543 pub fn checked_bitxor_assign(
544 &self,
545 ct_left: &mut RadixCiphertext,
546 ct_right: &RadixCiphertext,
547 ) -> Result<(), CheckError> {
548 if self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
549 self.unchecked_bitxor_assign(ct_left, ct_right);
550 Ok(())
551 } else {
552 Err(CarryFull)
553 }
554 }
555
556 /// Computes homomorphically a bitxor between two ciphertexts encrypting integer values.
557 ///
558 /// # Example
559 ///
560 /// ```rust
561 /// use concrete_integer::gen_keys_radix;
562 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
563 ///
564 /// let size = 4;
565 ///
566 /// // Generate the client key and the server key:
567 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
568 ///
569 /// let msg1 = 14;
570 /// let msg2 = 97;
571 ///
572 /// let mut ct1 = cks.encrypt(msg1);
573 /// let mut ct2 = cks.encrypt(msg2);
574 ///
575 /// let ct_res = sks.smart_bitxor(&mut ct1, &mut ct2);
576 ///
577 /// // Decrypt:
578 /// let dec_result = cks.decrypt(&ct_res);
579 /// assert_eq!(dec_result, msg1 ^ msg2);
580 /// ```
581 pub fn smart_bitxor(
582 &self,
583 ct_left: &mut RadixCiphertext,
584 ct_right: &mut RadixCiphertext,
585 ) -> RadixCiphertext {
586 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
587 self.full_propagate(ct_left);
588 self.full_propagate(ct_right);
589 }
590 self.unchecked_bitxor(ct_left, ct_right)
591 }
592
593 pub fn smart_bitxor_assign(
594 &self,
595 ct_left: &mut RadixCiphertext,
596 ct_right: &mut RadixCiphertext,
597 ) {
598 if !self.is_functional_bivariate_pbs_possible(ct_left, ct_right) {
599 self.full_propagate(ct_left);
600 self.full_propagate(ct_right);
601 }
602 self.unchecked_bitxor_assign(ct_left, ct_right);
603 }
604}