concrete_integer/server_key/radix/
mul.rs

1use crate::ciphertext::RadixCiphertext;
2use crate::ServerKey;
3
4impl ServerKey {
5    /// Computes homomorphically a multiplication between a ciphertext encrypting an integer value
6    /// and another encrypting a shortint value.
7    ///
8    /// This function computes the operation without checking if it exceeds the capacity of the
9    /// ciphertext.
10    ///
11    /// The result is assigned to the `ct_left` ciphertext.
12    ///
13    /// # Example
14    ///
15    ///```rust
16    /// use concrete_integer::gen_keys_radix;
17    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
18    /// let size = 4;
19    ///
20    /// // Generate the client key and the server key:
21    /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
22    ///
23    /// let clear_1 = 170;
24    /// let clear_2 = 3;
25    ///
26    /// // Encrypt two messages
27    /// let mut ct_left = cks.encrypt(clear_1);
28    /// let ct_right = cks.encrypt_one_block(clear_2);
29    ///
30    /// // Compute homomorphically a multiplication
31    /// sks.unchecked_block_mul_assign(&mut ct_left, &ct_right, 0);
32    ///
33    /// // Decrypt
34    /// let res = cks.decrypt(&ct_left);
35    /// assert_eq!((clear_1 * clear_2) % 256, res);
36    /// ```
37    pub fn unchecked_block_mul_assign(
38        &self,
39        ct_left: &mut RadixCiphertext,
40        ct_right: &concrete_shortint::Ciphertext,
41        index: usize,
42    ) {
43        *ct_left = self.unchecked_block_mul(ct_left, ct_right, index);
44    }
45
46    /// Computes homomorphically a multiplication between a ciphertexts encrypting an integer
47    /// value and another encrypting a shortint value.
48    ///
49    /// This function computes the operation without checking if it exceeds the capacity of the
50    /// ciphertext.
51    ///
52    /// The result is returned as a new ciphertext.
53    ///
54    /// # Example
55    ///
56    ///```rust
57    /// use concrete_integer::gen_keys_radix;
58    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
59    /// let size = 4;
60    ///
61    /// // Generate the client key and the server key:
62    /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
63    ///
64    /// let clear_1 = 55;
65    /// let clear_2 = 3;
66    ///
67    /// // Encrypt two messages
68    /// let ct_left = cks.encrypt(clear_1);
69    /// let ct_right = cks.encrypt_one_block(clear_2);
70    ///
71    /// // Compute homomorphically a multiplication
72    /// let ct_res = sks.unchecked_block_mul(&ct_left, &ct_right, 0);
73    ///
74    /// // Decrypt
75    /// let res = cks.decrypt(&ct_res);
76    /// assert_eq!((clear_1 * clear_2) % 256, res);
77    /// ```
78    pub fn unchecked_block_mul(
79        &self,
80        ct1: &RadixCiphertext,
81        ct2: &concrete_shortint::Ciphertext,
82        index: usize,
83    ) -> RadixCiphertext {
84        let shifted_ct = self.blockshift(ct1, index);
85
86        let mut result_lsb = shifted_ct.clone();
87        let mut result_msb = shifted_ct;
88
89        for res_lsb_i in result_lsb.blocks[index..].iter_mut() {
90            self.key.unchecked_mul_lsb_assign(res_lsb_i, ct2);
91        }
92
93        let len = result_msb.blocks.len() - 1;
94        for res_msb_i in result_msb.blocks[index..len].iter_mut() {
95            self.key.unchecked_mul_msb_assign(res_msb_i, ct2);
96        }
97
98        result_msb = self.blockshift(&result_msb, 1);
99
100        self.unchecked_add(&result_lsb, &result_msb)
101    }
102
103    /// Computes homomorphically a multiplication between a ciphertext encrypting integer value
104    /// and another encrypting a shortint value.
105    ///
106    /// The result is returned as a new ciphertext.
107    ///
108    /// # Example
109    ///
110    ///```rust
111    /// use concrete_integer::gen_keys_radix;
112    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
113    /// let size = 4;
114    ///
115    /// // Generate the client key and the server key:
116    /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
117    ///
118    /// let clear_1 = 170;
119    /// let clear_2 = 3;
120    ///
121    /// // Encrypt two messages
122    /// let mut ctxt_1 = cks.encrypt(clear_1);
123    /// let ctxt_2 = cks.encrypt_one_block(clear_2);
124    ///
125    /// // Compute homomorphically a multiplication
126    /// let ct_res = sks.smart_block_mul(&mut ctxt_1, &ctxt_2, 0);
127    ///
128    /// // Decrypt
129    /// let res = cks.decrypt(&ct_res);
130    /// assert_eq!((clear_1 * clear_2) % 256, res);
131    /// ```
132    pub fn smart_block_mul(
133        &self,
134        ct1: &mut RadixCiphertext,
135        ct2: &concrete_shortint::Ciphertext,
136        index: usize,
137    ) -> RadixCiphertext {
138        //Makes sure we can do the multiplications
139        self.full_propagate(ct1);
140
141        let shifted_ct = self.blockshift(ct1, index);
142
143        let mut result_lsb = shifted_ct.clone();
144        let mut result_msb = shifted_ct;
145
146        for res_lsb_i in result_lsb.blocks[index..].iter_mut() {
147            self.key.unchecked_mul_lsb_assign(res_lsb_i, ct2);
148        }
149
150        let len = result_msb.blocks.len() - 1;
151        for res_msb_i in result_msb.blocks[index..len].iter_mut() {
152            self.key.unchecked_mul_msb_assign(res_msb_i, ct2);
153        }
154
155        result_msb = self.blockshift(&result_msb, 1);
156
157        self.smart_add(&mut result_lsb, &mut result_msb)
158    }
159
160    pub fn smart_block_mul_assign(
161        &self,
162        ct1: &mut RadixCiphertext,
163        ct2: &concrete_shortint::Ciphertext,
164        index: usize,
165    ) {
166        *ct1 = self.smart_block_mul(ct1, ct2, index);
167    }
168
169    /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
170    ///
171    /// This function computes the operation without checking if it exceeds the capacity of the
172    /// ciphertext.
173    ///
174    /// The result is assigned to the `ct_left` ciphertext.
175    /// # Example
176    ///
177    /// ```rust
178    /// use concrete_integer::gen_keys_radix;
179    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
180    /// let size = 4;
181    ///
182    /// // Generate the client key and the server key:
183    /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
184    ///
185    /// let clear_1 = 255;
186    /// let clear_2 = 143;
187    ///
188    /// // Encrypt two messages
189    /// let mut ctxt_1 = cks.encrypt(clear_1);
190    /// let ctxt_2 = cks.encrypt(clear_2);
191    ///
192    /// // Compute homomorphically a multiplication
193    /// let ct_res = sks.unchecked_mul(&mut ctxt_1, &ctxt_2);
194    ///
195    /// // Decrypt
196    /// let res = cks.decrypt(&ct_res);
197    /// assert_eq!((clear_1 * clear_2) % 256, res);
198    /// ```
199    pub fn unchecked_mul_assign(&self, ct1: &mut RadixCiphertext, ct2: &RadixCiphertext) {
200        *ct1 = self.unchecked_mul(ct1, ct2);
201    }
202
203    /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
204    ///
205    /// This function computes the operation without checking if it exceeds the capacity of the
206    /// ciphertext.
207    ///
208    /// The result is returned as a new ciphertext.
209    pub fn unchecked_mul(
210        &self,
211        ct1: &mut RadixCiphertext,
212        ct2: &RadixCiphertext,
213    ) -> RadixCiphertext {
214        let copy = ct1.clone();
215        let mut result = self.create_trivial_zero_radix(ct1.blocks.len());
216
217        for (i, ct2_i) in ct2.blocks.iter().enumerate() {
218            let tmp = self.unchecked_block_mul(&copy, ct2_i, i);
219
220            self.unchecked_add_assign(&mut result, &tmp);
221        }
222
223        result
224    }
225
226    /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
227    ///
228    /// The result is assigned to the `ct_left` ciphertext.
229    ///
230    /// # Example
231    ///
232    /// ```rust
233    /// use concrete_integer::gen_keys_radix;
234    /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
235    /// let size = 4;
236    ///
237    /// // Generate the client key and the server key:
238    /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, size);
239    ///
240    /// let clear_1 = 170;
241    /// let clear_2 = 6;
242    ///
243    /// // Encrypt two messages
244    /// let mut ctxt_1 = cks.encrypt(clear_1);
245    /// let mut ctxt_2 = cks.encrypt(clear_2);
246    ///
247    /// // Compute homomorphically a multiplication
248    /// let ct_res = sks.smart_mul(&mut ctxt_1, &mut ctxt_2);
249    /// // Decrypt
250    /// let res = cks.decrypt(&ct_res);
251    /// assert_eq!((clear_1 * clear_2) % 256, res);
252    /// ```
253    pub fn smart_mul_assign(&self, ct1: &mut RadixCiphertext, ct2: &mut RadixCiphertext) {
254        *ct1 = self.smart_mul(ct1, ct2);
255    }
256
257    /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
258    ///
259    /// The result is returned as a new ciphertext.
260    pub fn smart_mul(
261        &self,
262        ct1: &mut RadixCiphertext,
263        ct2: &mut RadixCiphertext,
264    ) -> RadixCiphertext {
265        self.full_propagate(ct1);
266        self.full_propagate(ct2);
267
268        let copy = ct1.clone();
269        let mut result = self.create_trivial_zero_radix(ct1.blocks.len());
270
271        for (i, ct2_i) in ct2.blocks.iter().enumerate() {
272            let mut tmp = self.unchecked_block_mul(&copy, ct2_i, i);
273            self.smart_add_assign(&mut result, &mut tmp);
274        }
275
276        result
277    }
278}