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