concrete_integer/server_key/radix/scalar_add.rs
1use crate::ciphertext::RadixCiphertext;
2use crate::server_key::CheckError;
3use crate::server_key::CheckError::CarryFull;
4use crate::ServerKey;
5
6impl ServerKey {
7 /// Computes homomorphically an addition between a scalar and a ciphertext.
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 msg = 4;
25 /// let scalar = 40;
26 ///
27 /// let ct = cks.encrypt(msg);
28 ///
29 /// // Compute homomorphically an addition:
30 /// let ct_res = sks.unchecked_scalar_add(&ct, scalar);
31 ///
32 /// // Decrypt:
33 /// let dec = cks.decrypt(&ct_res);
34 /// assert_eq!(msg + scalar, dec);
35 /// ```
36 pub fn unchecked_scalar_add(&self, ct: &RadixCiphertext, scalar: u64) -> RadixCiphertext {
37 let mut result = ct.clone();
38 self.unchecked_scalar_add_assign(&mut result, scalar);
39 result
40 }
41
42 /// Computes homomorphically an addition between a scalar and a ciphertext.
43 ///
44 /// This function computes the operation without checking if it exceeds the capacity of the
45 /// ciphertext.
46 ///
47 /// The result is assigned to the `ct_left` ciphertext.
48 pub fn unchecked_scalar_add_assign(&self, ct: &mut RadixCiphertext, scalar: u64) {
49 // Bits of message put to 1
50 let mask = (self.key.message_modulus.0 - 1) as u64;
51
52 let mut power = 1_u64;
53 // Put each decomposition into a new ciphertext
54 for ct_i in ct.blocks.iter_mut() {
55 let mut decomp = scalar & (mask * power);
56 decomp /= power;
57
58 self.key.unchecked_scalar_add_assign(ct_i, decomp as u8);
59
60 //modulus to the power i
61 power *= self.key.message_modulus.0 as u64;
62 }
63 }
64
65 /// Verifies if a scalar can be added to a ciphertext.
66 ///
67 /// # Example
68 ///
69 ///```rust
70 /// use concrete_integer::gen_keys_radix;
71 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
72 ///
73 /// // We have 4 * 2 = 8 bits of message
74 /// let size = 4;
75 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
76 ///
77 /// let msg = 2;
78 /// let scalar = 40;
79 ///
80 /// // Encrypt two messages:
81 /// let ct1 = cks.encrypt(msg);
82 /// let ct2 = cks.encrypt(msg);
83 ///
84 /// // Check if we can perform an addition
85 /// let res = sks.is_scalar_add_possible(&ct1, scalar);
86 ///
87 /// assert_eq!(true, res);
88 /// ```
89 pub fn is_scalar_add_possible(&self, ct: &RadixCiphertext, scalar: u64) -> bool {
90 //Bits of message put to 1
91 let mask = (self.key.message_modulus.0 - 1) as u64;
92
93 let mut power = 1_u64;
94
95 for ct_i in ct.blocks.iter() {
96 let mut decomp = scalar & (mask * power);
97 decomp /= power;
98
99 if !self.key.is_scalar_add_possible(ct_i, decomp as u8) {
100 return false;
101 }
102
103 //modulus to the power i
104 power *= self.key.message_modulus.0 as u64;
105 }
106 true
107 }
108
109 /// Computes homomorphically an addition between a scalar and a ciphertext.
110 ///
111 /// If the operation can be performed, the result is returned in a new ciphertext.
112 /// Otherwise [CheckError::CarryFull] is returned.
113 ///
114 /// # Example
115 ///
116 /// ```rust
117 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
118 /// use concrete_integer::gen_keys_radix;
119 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
120 ///
121 /// // We have 4 * 2 = 8 bits of message
122 /// let size = 4;
123 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
124 ///
125 /// let msg = 4;
126 /// let scalar = 40;
127 ///
128 /// let mut ct = cks.encrypt(msg);
129 ///
130 /// // Compute homomorphically an addition:
131 /// let ct_res = sks.checked_scalar_add(&mut ct, scalar)?;
132 ///
133 /// // Decrypt:
134 /// let dec = cks.decrypt(&ct_res);
135 /// assert_eq!(msg + scalar, dec);
136 /// # Ok(())
137 /// # }
138 /// ```
139 pub fn checked_scalar_add(
140 &self,
141 ct: &RadixCiphertext,
142 scalar: u64,
143 ) -> Result<RadixCiphertext, CheckError> {
144 if self.is_scalar_add_possible(ct, scalar) {
145 Ok(self.unchecked_scalar_add(ct, scalar))
146 } else {
147 Err(CarryFull)
148 }
149 }
150
151 /// Computes homomorphically an addition between a scalar and a ciphertext.
152 ///
153 /// If the operation can be performed, the result is stored in the `ct_left` ciphertext.
154 /// Otherwise [CheckError::CarryFull] is returned, and `ct_left` is not modified.
155 pub fn checked_scalar_add_assign(
156 &self,
157 ct: &mut RadixCiphertext,
158 scalar: u64,
159 ) -> Result<(), CheckError> {
160 if self.is_scalar_add_possible(ct, scalar) {
161 self.unchecked_scalar_add_assign(ct, scalar);
162 Ok(())
163 } else {
164 Err(CarryFull)
165 }
166 }
167
168 /// Computes homomorphically the addition of ciphertext with a scalar.
169 ///
170 /// The result is returned in a new ciphertext.
171 ///
172 /// # Example
173 ///
174 /// ```rust
175 /// use concrete_integer::gen_keys_radix;
176 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
177 ///
178 /// // We have 4 * 2 = 8 bits of message
179 /// let size = 4;
180 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
181 ///
182 /// let msg = 4;
183 /// let scalar = 40;
184 ///
185 /// let mut ct = cks.encrypt(msg);
186 ///
187 /// // Compute homomorphically an addition:
188 /// let ct_res = sks.smart_scalar_add(&mut ct, scalar);
189 ///
190 /// // Decrypt:
191 /// let dec = cks.decrypt(&ct_res);
192 /// assert_eq!(msg + scalar, dec);
193 /// ```
194 pub fn smart_scalar_add(&self, ct: &mut RadixCiphertext, scalar: u64) -> RadixCiphertext {
195 if !self.is_scalar_add_possible(ct, scalar) {
196 self.full_propagate(ct);
197 }
198
199 let mut ct = ct.clone();
200 self.unchecked_scalar_add_assign(&mut ct, scalar);
201 ct
202 }
203
204 /// Computes homomorphically the addition of ciphertext with a scalar.
205 ///
206 /// The result is assigned to the `ct_left` ciphertext.
207 ///
208 /// # Example
209 ///
210 /// ```rust
211 /// use concrete_integer::gen_keys_radix;
212 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
213 ///
214 /// // We have 4 * 2 = 8 bits of message
215 /// let size = 4;
216 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
217 ///
218 /// let msg = 129;
219 /// let scalar = 40;
220 ///
221 /// let mut ct = cks.encrypt(msg);
222 ///
223 /// // Compute homomorphically an addition:
224 /// sks.smart_scalar_add_assign(&mut ct, scalar);
225 ///
226 /// // Decrypt:
227 /// let dec = cks.decrypt(&ct);
228 /// assert_eq!(msg + scalar, dec);
229 /// ```
230 pub fn smart_scalar_add_assign(&self, ct: &mut RadixCiphertext, scalar: u64) {
231 if !self.is_scalar_add_possible(ct, scalar) {
232 self.full_propagate(ct);
233 }
234 self.unchecked_scalar_add_assign(ct, scalar);
235 }
236}