concrete_shortint/server_key/
add.rs

1use super::ServerKey;
2use crate::engine::ShortintEngine;
3use crate::server_key::CheckError;
4use crate::server_key::CheckError::CarryFull;
5use crate::Ciphertext;
6
7impl ServerKey {
8    /// Computes homomorphically an addition between two ciphertexts encrypting integer values.
9    ///
10    /// The result is returned in a _new_ ciphertext.
11    ///
12    /// This function computes the addition without checking if it exceeds the capacity of the
13    /// ciphertext.
14    ///
15    /// # Example
16    ///
17    /// ```rust
18    /// use concrete_shortint::gen_keys;
19    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
20    ///
21    /// // Generate the client key and the server key:
22    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
23    ///
24    /// let msg1 = 1;
25    /// let msg2 = 2;
26    /// let ct1 = cks.encrypt(msg1);
27    /// let ct2 = cks.encrypt(msg2);
28    ///
29    /// // Compute homomorphically an addition:
30    /// let ct_res = sks.unchecked_add(&ct1, &ct2);
31    ///
32    /// // Decrypt:
33    /// let res = cks.decrypt(&ct_res);
34    /// assert_eq!(msg1 + msg2, res);
35    /// ```
36    pub fn unchecked_add(&self, ct_left: &Ciphertext, ct_right: &Ciphertext) -> Ciphertext {
37        ShortintEngine::with_thread_local_mut(|engine| {
38            engine.unchecked_add(ct_left, ct_right).unwrap()
39        })
40    }
41
42    /// Computes homomorphically an addition between two ciphertexts encrypting integer values.
43    ///
44    /// The result is _stored_ in the `ct_left` ciphertext.
45    ///
46    /// This function computes the addition without checking if it exceeds the capacity of the
47    /// ciphertext.
48    ///
49    /// # Example
50    ///
51    /// ```rust
52    /// use concrete_shortint::gen_keys;
53    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
54    ///
55    /// // Generate the client key and the server key:
56    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
57    ///
58    /// let msg = 1;
59    ///
60    /// let mut ct_left = cks.encrypt(msg);
61    /// let ct_right = cks.encrypt(msg);
62    ///
63    /// // Compute homomorphically an addition:
64    /// sks.unchecked_add_assign(&mut ct_left, &ct_right);
65    ///
66    /// // Decrypt:
67    /// let two = cks.decrypt(&ct_left);
68    /// assert_eq!(msg + msg, two);
69    /// ```
70    pub fn unchecked_add_assign(&self, ct_left: &mut Ciphertext, ct_right: &Ciphertext) {
71        ShortintEngine::with_thread_local_mut(|engine| {
72            engine.unchecked_add_assign(ct_left, ct_right).unwrap()
73        })
74    }
75
76    /// Verifies if ct_left and ct_right can be added together.
77    ///
78    /// This checks that the sum of their degree is
79    /// smaller than the maximum degree.
80    ///
81    /// # Example
82    ///
83    ///```rust
84    /// use concrete_shortint::gen_keys;
85    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
86    ///
87    /// // Generate the client key and the server key:
88    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
89    ///
90    /// let msg = 2;
91    ///
92    /// // Encrypt two messages:
93    /// let ct_left = cks.encrypt(msg);
94    /// let ct_right = cks.encrypt(msg);
95    ///
96    /// // Check if we can perform an addition
97    /// let can_be_added = sks.is_add_possible(&ct_left, &ct_right);
98    ///
99    /// assert_eq!(can_be_added, true);
100    /// ```
101    pub fn is_add_possible(&self, ct_left: &Ciphertext, ct_right: &Ciphertext) -> bool {
102        let final_operation_count = ct_left.degree.0 + ct_right.degree.0;
103        final_operation_count <= self.max_degree.0
104    }
105
106    /// Computes homomorphically an addition between two ciphertexts encrypting integer values.
107    ///
108    /// If the operation can be performed, the result is returned a _new_ ciphertext.
109    /// Otherwise [CheckError::CarryFull] is returned.
110    ///
111    /// # Example
112    ///
113    /// ```rust
114    /// use concrete_shortint::gen_keys;
115    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
116    ///
117    /// // Generate the client key and the server key:
118    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
119    ///
120    /// let msg = 1;
121    ///
122    /// // Encrypt two messages:
123    /// let ct1 = cks.encrypt(msg);
124    /// let ct2 = cks.encrypt(msg);
125    ///
126    /// // Compute homomorphically an addition:
127    /// let ct_res = sks.checked_add(&ct1, &ct2);
128    ///
129    /// assert!(ct_res.is_ok());
130    ///
131    /// let ct_res = ct_res.unwrap();
132    /// let clear_res = cks.decrypt(&ct_res);
133    /// assert_eq!(clear_res, msg + msg);
134    /// ```
135    pub fn checked_add(
136        &self,
137        ct_left: &Ciphertext,
138        ct_right: &Ciphertext,
139    ) -> Result<Ciphertext, CheckError> {
140        if self.is_add_possible(ct_left, ct_right) {
141            let ct_result = self.unchecked_add(ct_left, ct_right);
142            Ok(ct_result)
143        } else {
144            Err(CarryFull)
145        }
146    }
147
148    /// Computes homomorphically an addition between two ciphertexts encrypting integer values.
149    ///
150    /// If the operation can be performed, the result is stored in the `ct_left` ciphertext.
151    /// Otherwise [CheckError::CarryFull] is returned, and `ct_left` is not modified.
152    ///
153    /// # Example
154    ///
155    /// ```rust
156    /// use concrete_shortint::gen_keys;
157    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
158    ///
159    /// // Generate the client key and the server key:
160    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
161    ///
162    /// let msg = 1;
163    ///
164    /// // Encrypt two messages:
165    /// let mut ct_left = cks.encrypt(msg);
166    /// let ct_right = cks.encrypt(msg);
167    ///
168    /// // Compute homomorphically an addition:
169    /// let res = sks.checked_add_assign(&mut ct_left, &ct_right);
170    ///
171    /// assert!(res.is_ok());
172    ///
173    /// let clear_res = cks.decrypt(&ct_left);
174    /// assert_eq!(clear_res, msg + msg);
175    /// ```
176    pub fn checked_add_assign(
177        &self,
178        ct_left: &mut Ciphertext,
179        ct_right: &Ciphertext,
180    ) -> Result<(), CheckError> {
181        if self.is_add_possible(ct_left, ct_right) {
182            self.unchecked_add_assign(ct_left, ct_right);
183            Ok(())
184        } else {
185            Err(CarryFull)
186        }
187    }
188
189    /// Computes homomorphically an addition between two ciphertexts encrypting integer values.
190    ///
191    /// This checks that the addition is possible. In the case where the carry buffers are full,
192    /// then it is automatically cleared to allow the operation.
193    /// # Example
194    ///
195    /// ```rust
196    /// use concrete_shortint::gen_keys;
197    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
198    ///
199    /// // Generate the client key and the server key:
200    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
201    ///
202    /// let msg = 1;
203    ///
204    /// // Encrypt two messages:
205    /// let mut ct1 = cks.encrypt(msg);
206    /// let mut ct2 = cks.encrypt(msg);
207    ///
208    /// // Compute homomorphically an addition:
209    /// let ct_res = sks.smart_add(&mut ct1, &mut ct2);
210    ///
211    /// // Decrypt:
212    /// let two = cks.decrypt(&ct_res);
213    /// assert_eq!(msg + msg, two);
214    /// ```
215    pub fn smart_add(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) -> Ciphertext {
216        ShortintEngine::with_thread_local_mut(|engine| {
217            engine.smart_add(self, ct_left, ct_right).unwrap()
218        })
219    }
220
221    /// Computes homomorphically an addition between two ciphertexts
222    ///
223    /// The result is stored in the `ct_left` cipher text.
224    ///
225    /// # Example
226    ///
227    /// ```rust
228    /// use concrete_shortint::gen_keys;
229    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
230    ///
231    /// // Generate the client key and the server key:
232    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
233    ///
234    /// // Encrypt two messages:
235    /// let msg1 = 15;
236    /// let msg2 = 3;
237    ///
238    /// let mut ct1 = cks.unchecked_encrypt(msg1);
239    /// let mut ct2 = cks.encrypt(msg2);
240    ///
241    /// // Compute homomorphically an addition:
242    /// sks.smart_add_assign(&mut ct1, &mut ct2);
243    ///
244    /// // Decrypt:
245    /// let two = cks.decrypt(&ct1);
246    ///
247    /// // 15 + 3 mod 4 -> 3 + 3 mod 4 -> 2 mod 4
248    /// let modulus = cks.parameters.message_modulus.0 as u64;
249    /// assert_eq!((msg2 + msg1) % modulus, two);
250    /// ```
251    pub fn smart_add_assign(&self, ct_left: &mut Ciphertext, ct_right: &mut Ciphertext) {
252        ShortintEngine::with_thread_local_mut(|engine| {
253            engine.smart_add_assign(self, ct_left, ct_right).unwrap()
254        })
255    }
256}