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}