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}