concrete_shortint/server_key/
shift.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 a right shift of the bits without checks.
9    ///
10    /// # Example
11    ///
12    /// ```rust
13    /// use concrete_shortint::gen_keys;
14    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
15    ///
16    /// // Generate the client key and the server key:
17    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
18    ///
19    /// let msg = 2;
20    /// let ct = cks.encrypt(msg);
21    /// // |       ct        |
22    /// // | carry | message |
23    /// // |-------|---------|
24    /// // |  0 0  |   1 0   |
25    ///
26    /// // Compute homomorphically a right shift
27    /// let shift: u8 = 1;
28    /// let ct_res = sks.unchecked_scalar_right_shift(&ct, shift);
29    /// // |      ct_res     |
30    /// // | carry | message |
31    /// // |-------|---------|
32    /// // |  0 0  |   0 1   |
33    ///
34    /// // Decrypt:
35    /// let dec = cks.decrypt(&ct_res);
36    /// let modulus = cks.parameters.message_modulus.0 as u64;
37    /// assert_eq!(msg >> shift, dec);
38    /// ```
39    pub fn unchecked_scalar_right_shift(&self, ct: &Ciphertext, shift: u8) -> Ciphertext {
40        ShortintEngine::with_thread_local_mut(|engine| {
41            engine
42                .unchecked_scalar_right_shift(self, ct, shift)
43                .unwrap()
44        })
45    }
46
47    /// Computes homomorphically a right shift of the bits without checks.
48    ///
49    /// # Example
50    ///
51    /// ```rust
52    /// use concrete_shortint::gen_keys;
53    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
54    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
55    ///
56    /// let msg = 2;
57    /// let mut ct = cks.encrypt(msg);
58    /// // |       ct        |
59    /// // | carry | message |
60    /// // |-------|---------|
61    /// // |  0 0  |   1 0   |
62    ///
63    /// // Compute homomorphically a right shift
64    /// let shift: u8 = 1;
65    /// sks.unchecked_scalar_right_shift_assign(&mut ct, shift);
66    /// // |       ct        |
67    /// // | carry | message |
68    /// // |-------|---------|
69    /// // |  0 0  |   0 1   |
70    ///
71    /// // Decrypt:
72    /// let dec = cks.decrypt(&ct);
73    /// let modulus = cks.parameters.message_modulus.0 as u64;
74    /// assert_eq!(msg >> shift, dec);
75    /// ```
76    pub fn unchecked_scalar_right_shift_assign(&self, ct: &mut Ciphertext, shift: u8) {
77        ShortintEngine::with_thread_local_mut(|engine| {
78            engine
79                .unchecked_scalar_right_shift_assign(self, ct, shift)
80                .unwrap()
81        })
82    }
83
84    /// Computes homomorphically a left shift of the bits without checks.
85    ///
86    /// # Example
87    ///
88    /// ```rust
89    /// use concrete_shortint::gen_keys;
90    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
91    ///
92    /// // Generate the client key and the server key:
93    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
94    ///
95    /// let msg = 2;
96    ///
97    /// let ct = cks.encrypt(msg);
98    /// // |       ct        |
99    /// // | carry | message |
100    /// // |-------|---------|
101    /// // |  0 0  |   1 0   |
102    ///
103    /// // Compute homomorphically a left shift
104    /// let shift: u8 = 1;
105    /// let ct_res = sks.unchecked_scalar_left_shift(&ct, shift);
106    /// // |      ct_res     |
107    /// // | carry | message |
108    /// // |-------|---------|
109    /// // |  0 1  |   0 0   |
110    ///
111    /// // Decrypt:
112    /// let msg_and_carry = cks.decrypt_message_and_carry(&ct_res);
113    /// let msg_only = cks.decrypt(&ct_res);
114    /// let modulus = cks.parameters.message_modulus.0 as u64;
115    ///
116    /// assert_eq!(msg << shift, msg_and_carry);
117    /// assert_eq!((msg << shift) % modulus, msg_only);
118    /// ```
119    pub fn unchecked_scalar_left_shift(&self, ct: &Ciphertext, shift: u8) -> Ciphertext {
120        ShortintEngine::with_thread_local_mut(|engine| {
121            engine.unchecked_scalar_left_shift(ct, shift).unwrap()
122        })
123    }
124
125    /// Computes homomorphically a left shift of the bits without checks
126    ///
127    /// # Example
128    ///
129    /// ```rust
130    /// use concrete_shortint::gen_keys;
131    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
132    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
133    ///
134    /// let msg = 2;
135    /// let mut ct = cks.encrypt(msg);
136    /// // |       ct        |
137    /// // | carry | message |
138    /// // |-------|---------|
139    /// // |  0 0  |   1 0   |
140    ///
141    /// // Compute homomorphically a left shift
142    /// let shift: u8 = 1;
143    /// sks.unchecked_scalar_left_shift_assign(&mut ct, shift);
144    /// // |      ct     |
145    /// // | carry | message |
146    /// // |-------|---------|
147    /// // |  0 1  |   0 0   |
148    ///
149    /// // Decrypt:
150    /// let msg_and_carry = cks.decrypt_message_and_carry(&ct);
151    /// let msg_only = cks.decrypt(&ct);
152    /// let modulus = cks.parameters.message_modulus.0 as u64;
153    ///
154    /// assert_eq!(msg << shift, msg_and_carry);
155    /// assert_eq!((msg << shift) % modulus, msg_only);
156    /// ```
157    pub fn unchecked_scalar_left_shift_assign(&self, ct: &mut Ciphertext, shift: u8) {
158        ShortintEngine::with_thread_local_mut(|engine| {
159            engine
160                .unchecked_scalar_left_shift_assign(ct, shift)
161                .unwrap()
162        })
163    }
164
165    /// Checks if the left shift operation can be applied.
166    ///
167    /// # Example
168    ///
169    ///```rust
170    /// use concrete_shortint::{gen_keys, Parameters};
171    ///
172    /// // Generate the client key and the server key:
173    /// let (cks, sks) = gen_keys(Parameters::default());
174    ///
175    /// let msg = 2;
176    /// let shift = 5;
177    /// let ct1 = cks.encrypt(msg);
178    ///
179    /// // Check if we can perform an addition
180    /// let res = sks.is_scalar_left_shift_possible(&ct1, shift);
181    ///
182    /// assert_eq!(false, res);
183    /// ```
184    pub fn is_scalar_left_shift_possible(&self, ct1: &Ciphertext, shift: u8) -> bool {
185        let final_operation_count = ct1.degree.0 << shift as usize;
186        final_operation_count <= self.max_degree.0
187    }
188
189    /// Computes homomorphically a left shift of the bits.
190    ///
191    /// If the operation can be performed, a new ciphertext with the result is returned.
192    /// Otherwise [CheckError::CarryFull] is returned.
193    ///
194    /// # Example
195    ///
196    /// ```rust
197    /// use concrete_shortint::gen_keys;
198    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
199    ///
200    /// // Generate the client key and the server key:
201    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
202    ///
203    /// let msg = 2;
204    ///
205    /// let ct1 = cks.encrypt(msg);
206    /// // |       ct        |
207    /// // | carry | message |
208    /// // |-------|---------|
209    /// // |  0 0  |   1 0   |
210    ///
211    /// // Shifting 3 times is not ok, as it exceeds the carry buffer
212    /// let ct_res = sks.checked_scalar_left_shift(&ct1, 3);
213    /// assert!(ct_res.is_err());
214    ///
215    /// // Shifting 2 times is ok
216    /// let shift = 2;
217    /// let ct_res = sks.checked_scalar_left_shift(&ct1, shift);
218    /// assert!(ct_res.is_ok());
219    /// let ct_res = ct_res.unwrap();
220    /// // |      ct_res     |
221    /// // | carry | message |
222    /// // |-------|---------|
223    /// // |  1 0  |   0 0   |
224    ///
225    /// // Decrypt:
226    /// let msg_and_carry = cks.decrypt_message_and_carry(&ct_res);
227    /// let msg_only = cks.decrypt(&ct_res);
228    /// let modulus = cks.parameters.message_modulus.0 as u64;
229    ///
230    /// assert_eq!(msg << shift, msg_and_carry);
231    /// assert_eq!((msg << shift) % modulus, msg_only);
232    /// ```
233    pub fn checked_scalar_left_shift(
234        &self,
235        ct: &Ciphertext,
236        shift: u8,
237    ) -> Result<Ciphertext, CheckError> {
238        if self.is_scalar_left_shift_possible(ct, shift) {
239            let ct_result = self.unchecked_scalar_left_shift(ct, shift);
240            Ok(ct_result)
241        } else {
242            Err(CarryFull)
243        }
244    }
245
246    pub fn checked_scalar_left_shift_assign(
247        &self,
248        ct: &mut Ciphertext,
249        shift: u8,
250    ) -> Result<(), CheckError> {
251        if self.is_scalar_left_shift_possible(ct, shift) {
252            self.unchecked_scalar_left_shift_assign(ct, shift);
253            Ok(())
254        } else {
255            Err(CarryFull)
256        }
257    }
258
259    /// Computes homomorphically a left shift of the bits
260    ///
261    /// This checks that the operation is possible. In the case where the carry buffers are
262    /// full, then it is automatically cleared to allow the operation.
263    ///
264    /// # Example
265    ///
266    /// ```rust
267    /// use concrete_shortint::gen_keys;
268    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
269    /// let (cks, sks) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
270    ///
271    /// let msg = 2;
272    /// let mut ct = cks.encrypt(msg);
273    /// // |       ct        |
274    /// // | carry | message |
275    /// // |-------|---------|
276    /// // |  0 0  |   1 0   |
277    ///
278    /// let shift: u8 = 1;
279    /// let ct_res = sks.smart_scalar_left_shift(&mut ct, shift);
280    /// // |      ct_res     |
281    /// // | carry | message |
282    /// // |-------|---------|
283    /// // |  0 1  |   0 0   |
284    ///
285    /// // Decrypt:
286    /// let msg_and_carry = cks.decrypt_message_and_carry(&ct_res);
287    /// let msg_only = cks.decrypt(&ct_res);
288    /// let modulus = cks.parameters.message_modulus.0 as u64;
289    ///
290    /// assert_eq!(msg << shift, msg_and_carry);
291    /// assert_eq!((msg << shift) % modulus, msg_only);
292    /// ```
293    pub fn smart_scalar_left_shift(&self, ct: &mut Ciphertext, shift: u8) -> Ciphertext {
294        ShortintEngine::with_thread_local_mut(|engine| {
295            engine.smart_scalar_left_shift(self, ct, shift).unwrap()
296        })
297    }
298
299    pub fn smart_scalar_left_shift_assign(&self, ct: &mut Ciphertext, shift: u8) {
300        ShortintEngine::with_thread_local_mut(|engine| {
301            engine
302                .smart_scalar_left_shift_assign(self, ct, shift)
303                .unwrap()
304        })
305    }
306}