1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT license.
3 
4 #pragma once
5 
6 #include "seal/ciphertext.h"
7 #include "seal/context.h"
8 #include "seal/galoiskeys.h"
9 #include "seal/memorymanager.h"
10 #include "seal/modulus.h"
11 #include "seal/plaintext.h"
12 #include "seal/relinkeys.h"
13 #include "seal/secretkey.h"
14 #include "seal/valcheck.h"
15 #include "seal/util/iterator.h"
16 #include <map>
17 #include <stdexcept>
18 #include <vector>
19 
20 namespace seal
21 {
22     /**
23     Provides operations on ciphertexts. Due to the properties of the encryption scheme, the arithmetic operations pass
24     through the encryption layer to the underlying plaintext, changing it according to the type of the operation. Since
25     the plaintext elements are fundamentally polynomials in the polynomial quotient ring Z_T[x]/(X^N+1), where T is the
26     plaintext modulus and X^N+1 is the polynomial modulus, this is the ring where the arithmetic operations will take
27     place. BatchEncoder (batching) provider an alternative possibly more convenient view of the plaintext elements as
28     2-by-(N2/2) matrices of integers modulo the plaintext modulus. In the batching view the arithmetic operations act on
29     the matrices element-wise. Some of the operations only apply in the batching view, such as matrix row and column
30     rotations. Other operations such as relinearization have no semantic meaning but are necessary for performance
31     reasons.
32 
33     @par Arithmetic Operations
34     The core operations are arithmetic operations, in particular multiplication and addition of ciphertexts. In addition
35     to these, we also provide negation, subtraction, squaring, exponentiation, and multiplication and addition of
36     several ciphertexts for convenience. in many cases some of the inputs to a computation are plaintext elements rather
37     than ciphertexts. For this we provide fast "plain" operations: plain addition, plain subtraction, and plain
38     multiplication.
39 
40     @par Relinearization
41     One of the most important non-arithmetic operations is relinearization, which takes as input a ciphertext of size
42     K+1 and relinearization keys (at least K-1 keys are needed), and changes the size of the ciphertext down to 2
43     (minimum size). For most use-cases only one relinearization key suffices, in which case relinearization should be
44     performed after every multiplication. Homomorphic multiplication of ciphertexts of size K+1 and L+1 outputs a
45     ciphertext of size K+L+1, and the computational cost of multiplication is proportional to K*L. Plain multiplication
46     and addition operations of any type do not change the size. Relinearization requires relinearization keys to have
47     been generated.
48 
49     @par Rotations
50     When batching is enabled, we provide operations for rotating the plaintext matrix rows cyclically left or right, and
51     for rotating the columns (swapping the rows). Rotations require Galois keys to have been generated.
52 
53     @par Other Operations
54     We also provide operations for transforming ciphertexts to NTT form and back, and for transforming plaintext
55     polynomials to NTT form. These can be used in a very fast plain multiplication variant, that assumes the inputs to
56     be in NTT form. Since the NTT has to be done in any case in plain multiplication, this function can be used when
57     e.g. one plaintext input is used in several plain multiplication, and transforming it several times would not make
58     sense.
59 
60     @par NTT form
61     When using the BFV scheme (scheme_type::bfv), all plaintexts and ciphertexts should remain by default in the usual
62     coefficient representation, i.e., not in NTT form. When using the CKKS scheme (scheme_type::ckks), all plaintexts
63     and ciphertexts should remain by default in NTT form. We call these scheme-specific NTT states the "default NTT
64     form". Some functions, such as add, work even if the inputs are not in the default state, but others, such as
65     multiply, will throw an exception. The output of all evaluation functions will be in the same state as the input(s),
66     with the exception of the transform_to_ntt and transform_from_ntt functions, which change the state. Ideally, unless
67     these two functions are called, all other functions should "just work".
68 
69     @see EncryptionParameters for more details on encryption parameters.
70     @see BatchEncoder for more details on batching
71     @see RelinKeys for more details on relinearization keys.
72     @see GaloisKeys for more details on Galois keys.
73     */
74     class Evaluator
75     {
76     public:
77         /**
78         Creates an Evaluator instance initialized with the specified SEALContext.
79 
80         @param[in] context The SEALContext
81         @throws std::invalid_argument if the encryption parameters are not valid
82         */
83         Evaluator(const SEALContext &context);
84 
85         /**
86         Negates a ciphertext.
87 
88         @param[in] encrypted The ciphertext to negate
89         @throws std::invalid_argument if encrypted is not valid for the encryption
90         parameters
91         */
92         void negate_inplace(Ciphertext &encrypted) const;
93 
94         /**
95         Negates a ciphertext and stores the result in the destination parameter.
96 
97         @param[in] encrypted The ciphertext to negate
98         @param[out] destination The ciphertext to overwrite with the negated result
99         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
100         @throws std::logic_error if result ciphertext is transparent
101         */
negate(const Ciphertext & encrypted,Ciphertext & destination)102         inline void negate(const Ciphertext &encrypted, Ciphertext &destination) const
103         {
104             destination = encrypted;
105             negate_inplace(destination);
106         }
107 
108         /**
109         Adds two ciphertexts. This function adds together encrypted1 and encrypted2 and stores the result in encrypted1.
110 
111         @param[in] encrypted1 The first ciphertext to add
112         @param[in] encrypted2 The second ciphertext to add
113         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
114         @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms
115         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale
116         @throws std::logic_error if result ciphertext is transparent
117         */
118         void add_inplace(Ciphertext &encrypted1, const Ciphertext &encrypted2) const;
119 
120         /**
121         Adds two ciphertexts. This function adds together encrypted1 and encrypted2 and stores the result in the
122         destination parameter.
123 
124         @param[in] encrypted1 The first ciphertext to add
125         @param[in] encrypted2 The second ciphertext to add
126         @param[out] destination The ciphertext to overwrite with the addition result
127         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
128         @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms
129         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale
130         @throws std::logic_error if result ciphertext is transparent
131         */
add(const Ciphertext & encrypted1,const Ciphertext & encrypted2,Ciphertext & destination)132         inline void add(const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination) const
133         {
134             if (&encrypted2 == &destination)
135             {
136                 add_inplace(destination, encrypted1);
137             }
138             else
139             {
140                 destination = encrypted1;
141                 add_inplace(destination, encrypted2);
142             }
143         }
144 
145         /**
146         Adds together a vector of ciphertexts and stores the result in the destination parameter.
147 
148         @param[in] encrypteds The ciphertexts to add
149         @param[out] destination The ciphertext to overwrite with the addition result
150         @throws std::invalid_argument if encrypteds is empty
151         @throws std::invalid_argument if encrypteds are not valid for the encryption
152         parameters
153         @throws std::invalid_argument if encrypteds are in different NTT forms
154         @throws std::invalid_argument if encrypteds are at different level or scale
155         @throws std::invalid_argument if destination is one of encrypteds
156         @throws std::logic_error if result ciphertext is transparent
157         */
158         void add_many(const std::vector<Ciphertext> &encrypteds, Ciphertext &destination) const;
159 
160         /**
161         Subtracts two ciphertexts. This function computes the difference of encrypted1 and encrypted2, and stores the
162         result in encrypted1.
163 
164         @param[in] encrypted1 The ciphertext to subtract from
165         @param[in] encrypted2 The ciphertext to subtract
166         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
167         @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms
168         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale
169         @throws std::logic_error if result ciphertext is transparent
170         */
171         void sub_inplace(Ciphertext &encrypted1, const Ciphertext &encrypted2) const;
172 
173         /**
174         Subtracts two ciphertexts. This function computes the difference of encrypted1 and encrypted2 and stores the
175         result in the destination parameter.
176 
177         @param[in] encrypted1 The ciphertext to subtract from
178         @param[in] encrypted2 The ciphertext to subtract
179         @param[out] destination The ciphertext to overwrite with the subtraction result
180         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
181         @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms
182         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale
183         @throws std::logic_error if result ciphertext is transparent
184         */
sub(const Ciphertext & encrypted1,const Ciphertext & encrypted2,Ciphertext & destination)185         inline void sub(const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination) const
186         {
187             if (&encrypted2 == &destination)
188             {
189                 sub_inplace(destination, encrypted1);
190                 negate_inplace(destination);
191             }
192             else
193             {
194                 destination = encrypted1;
195                 sub_inplace(destination, encrypted2);
196             }
197         }
198 
199         /**
200         Multiplies two ciphertexts. This functions computes the product of encrypted1 and encrypted2 and stores the
201         result in encrypted1. Dynamic memory allocations in the process are allocated from the memory pool pointed to by
202         the given MemoryPoolHandle.
203 
204         @param[in] encrypted1 The first ciphertext to multiply
205         @param[in] encrypted2 The second ciphertext to multiply
206         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
207         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
208         @throws std::invalid_argument if encrypted1 or encrypted2 is not in the default NTT form
209         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level
210         @throws std::invalid_argument if the output scale is too large for the encryption parameters
211         @throws std::invalid_argument if pool is uninitialized
212         @throws std::logic_error if result ciphertext is transparent
213         */
214         void multiply_inplace(
215             Ciphertext &encrypted1, const Ciphertext &encrypted2,
216             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
217 
218         /**
219         Multiplies two ciphertexts. This functions computes the product of encrypted1 and encrypted2 and stores the
220         result in the destination parameter. Dynamic memory allocations in the process are allocated from the memory
221         pool pointed to by the given MemoryPoolHandle.
222 
223         @param[in] encrypted1 The first ciphertext to multiply
224         @param[in] encrypted2 The second ciphertext to multiply
225         @param[out] destination The ciphertext to overwrite with the multiplication result
226         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
227         @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters
228         @throws std::invalid_argument if encrypted1 and encrypted2 are at different level
229         @throws std::invalid_argument if encrypted1 or encrypted2 is not in the default NTT form
230         @throws std::invalid_argument if the output scale is too large for the encryption parameters
231         @throws std::invalid_argument if pool is uninitialized
232         @throws std::logic_error if result ciphertext is transparent
233         */
234         inline void multiply(
235             const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination,
236             MemoryPoolHandle pool = MemoryManager::GetPool()) const
237         {
238             if (&encrypted2 == &destination)
239             {
240                 multiply_inplace(destination, encrypted1, std::move(pool));
241             }
242             else
243             {
244                 destination = encrypted1;
245                 multiply_inplace(destination, encrypted2, std::move(pool));
246             }
247         }
248 
249         /**
250         Squares a ciphertext. This functions computes the square of encrypted. Dynamic memory allocations in the process
251         are allocated from the memory pool pointed to by the given MemoryPoolHandle.
252 
253         @param[in] encrypted The ciphertext to square
254         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
255         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
256         @throws std::invalid_argument if encrypted is not in the default NTT form
257         @throws std::invalid_argument if the output scale is too large for the encryption parameters
258         @throws std::invalid_argument if pool is uninitialized
259         @throws std::logic_error if result ciphertext is transparent
260         */
261         void square_inplace(Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
262 
263         /**
264         Squares a ciphertext. This functions computes the square of encrypted and stores the result in the destination
265         parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
266         MemoryPoolHandle.
267 
268         @param[in] encrypted The ciphertext to square
269         @param[out] destination The ciphertext to overwrite with the square
270         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
271         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
272         @throws std::invalid_argument if encrypted is not in the default NTT form
273         @throws std::invalid_argument if the output scale is too large for the encryption parameters
274         @throws std::invalid_argument if pool is uninitialized
275         @throws std::logic_error if result ciphertext is transparent
276         */
277         inline void square(
278             const Ciphertext &encrypted, Ciphertext &destination,
279             MemoryPoolHandle pool = MemoryManager::GetPool()) const
280         {
281             destination = encrypted;
282             square_inplace(destination, std::move(pool));
283         }
284 
285         /**
286         Relinearizes a ciphertext. This functions relinearizes encrypted, reducing its size down to 2. If the size of
287         encrypted is K+1, the given relinearization keys need to have size at least K-1. Dynamic memory allocations in
288         the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
289 
290         @param[in] encrypted The ciphertext to relinearize
291         @param[in] relin_keys The relinearization keys
292         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
293         @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters
294         @throws std::invalid_argument if encrypted is not in the default NTT form
295         @throws std::invalid_argument if relin_keys do not correspond to the top level parameters in the current context
296         @throws std::invalid_argument if the size of relin_keys is too small
297         @throws std::invalid_argument if pool is uninitialized
298         @throws std::logic_error if keyswitching is not supported by the context
299         @throws std::logic_error if result ciphertext is transparent
300         */
301         inline void relinearize_inplace(
302             Ciphertext &encrypted, const RelinKeys &relin_keys, MemoryPoolHandle pool = MemoryManager::GetPool()) const
303         {
304             relinearize_internal(encrypted, relin_keys, 2, std::move(pool));
305         }
306 
307         /**
308         Relinearizes a ciphertext. This functions relinearizes encrypted, reducing its size down to 2, and stores the
309         result in the destination parameter. If the size of encrypted is K+1, the given relinearization keys need to
310         have size at least K-1. Dynamic memory allocations in the process are allocated from the memory pool pointed to
311         by the given MemoryPoolHandle.
312 
313         @param[in] encrypted The ciphertext to relinearize
314         @param[in] relin_keys The relinearization keys
315         @param[out] destination The ciphertext to overwrite with the relinearized result
316         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
317         @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters
318         @throws std::invalid_argument if encrypted is not in the default NTT form
319         @throws std::invalid_argument if relin_keys do not correspond to the top level parameters in the current context
320         @throws std::invalid_argument if the size of relin_keys is too small
321         @throws std::invalid_argument if pool is uninitialized
322         @throws std::logic_error if keyswitching is not supported by the context
323         @throws std::logic_error if result ciphertext is transparent
324         */
325         inline void relinearize(
326             const Ciphertext &encrypted, const RelinKeys &relin_keys, Ciphertext &destination,
327             MemoryPoolHandle pool = MemoryManager::GetPool()) const
328         {
329             destination = encrypted;
330             relinearize_inplace(destination, relin_keys, std::move(pool));
331         }
332 
333         /**
334         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
335         stores the result in the destination parameter. Dynamic memory allocations in the process are allocated from the
336         memory pool pointed to by the given MemoryPoolHandle.
337 
338         @param[in] encrypted The ciphertext to be switched to a smaller modulus
339         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
340         @param[out] destination The ciphertext to overwrite with the modulus switched result
341         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
342         @throws std::invalid_argument if encrypted is not in the default NTT form
343         @throws std::invalid_argument if encrypted is already at lowest level
344         @throws std::invalid_argument if the scale is too large for the new encryption parameters
345         @throws std::invalid_argument if pool is uninitialized
346         @throws std::logic_error if result ciphertext is transparent
347         */
348         void mod_switch_to_next(
349             const Ciphertext &encrypted, Ciphertext &destination,
350             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
351 
352         /**
353         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}. Dynamic
354         memory allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
355 
356         @param[in] encrypted The ciphertext to be switched to a smaller modulus
357         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
358         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
359         @throws std::invalid_argument if encrypted is not in the default NTT form
360         @throws std::invalid_argument if encrypted is already at lowest level
361         @throws std::invalid_argument if the scale is too large for the new encryption parameters
362         @throws std::invalid_argument if pool is uninitialized
363         @throws std::logic_error if result ciphertext is transparent
364         */
365         inline void mod_switch_to_next_inplace(
366             Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const
367         {
368             mod_switch_to_next(encrypted, encrypted, std::move(pool));
369         }
370 
371         /**
372         Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1}.
373 
374         @param[in] plain The plaintext to be switched to a smaller modulus
375         @throws std::invalid_argument if plain is not in NTT form
376         @throws std::invalid_argument if plain is not valid for the encryption parameters
377         @throws std::invalid_argument if plain is already at lowest level
378         @throws std::invalid_argument if the scale is too large for the new encryption parameters
379         */
mod_switch_to_next_inplace(Plaintext & plain)380         inline void mod_switch_to_next_inplace(Plaintext &plain) const
381         {
382             // Verify parameters.
383             if (!is_valid_for(plain, context_))
384             {
385                 throw std::invalid_argument("plain is not valid for encryption parameters");
386             }
387             mod_switch_drop_to_next(plain);
388         }
389 
390         /**
391         Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1} and stores the
392         result in the destination parameter.
393 
394         @param[in] plain The plaintext to be switched to a smaller modulus
395         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
396         @param[out] destination The plaintext to overwrite with the modulus switched result
397         @throws std::invalid_argument if plain is not in NTT form
398         @throws std::invalid_argument if plain is not valid for the encryption parameters
399         @throws std::invalid_argument if plain is already at lowest level
400         @throws std::invalid_argument if the scale is too large for the new encryption parameters
401         @throws std::invalid_argument if pool is uninitialized
402         */
mod_switch_to_next(const Plaintext & plain,Plaintext & destination)403         inline void mod_switch_to_next(const Plaintext &plain, Plaintext &destination) const
404         {
405             destination = plain;
406             mod_switch_to_next_inplace(destination);
407         }
408 
409         /**
410         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
411         reach the given parms_id. Dynamic memory allocations in the process are allocated from the memory pool pointed
412         to by the given MemoryPoolHandle.
413 
414         @param[in] encrypted The ciphertext to be switched to a smaller modulus
415         @param[in] parms_id The target parms_id
416         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
417         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
418         @throws std::invalid_argument if encrypted is not in the default NTT form
419         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
420         @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters
421         corresponding to parms_id
422         @throws std::invalid_argument if the scale is too large for the new encryption parameters
423         @throws std::invalid_argument if pool is uninitialized
424         @throws std::logic_error if result ciphertext is transparent
425         */
426         void mod_switch_to_inplace(
427             Ciphertext &encrypted, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
428 
429         /**
430         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
431         reach the given parms_id and stores the result in the destination parameter. Dynamic memory allocations in the
432         process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
433 
434         @param[in] encrypted The ciphertext to be switched to a smaller modulus
435         @param[in] parms_id The target parms_id
436         @param[out] destination The ciphertext to overwrite with the modulus switched result
437         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
438         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
439         @throws std::invalid_argument if encrypted is not in the default NTT form
440         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
441         @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters
442         corresponding to parms_id
443         @throws std::invalid_argument if the scale is too large for the new encryption parameters
444         @throws std::invalid_argument if pool is uninitialized
445         @throws std::logic_error if result ciphertext is transparent
446         */
447         inline void mod_switch_to(
448             const Ciphertext &encrypted, parms_id_type parms_id, Ciphertext &destination,
449             MemoryPoolHandle pool = MemoryManager::GetPool()) const
450         {
451             destination = encrypted;
452             mod_switch_to_inplace(destination, parms_id, std::move(pool));
453         }
454 
455         /**
456         Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the
457         parameters reach the given parms_id.
458 
459         @param[in] plain The plaintext to be switched to a smaller modulus
460         @param[in] parms_id The target parms_id
461         @throws std::invalid_argument if plain is not in NTT form
462         @throws std::invalid_argument if plain is not valid for the encryption parameters
463         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
464         @throws std::invalid_argument if plain is already at lower level in modulus chain than the parameters
465         corresponding to parms_id
466         @throws std::invalid_argument if the scale is too large for the new encryption parameters
467         */
468         void mod_switch_to_inplace(Plaintext &plain, parms_id_type parms_id) const;
469 
470         /**
471         Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the
472         parameters reach the given parms_id and stores the result in the destination parameter.
473 
474         @param[in] plain The plaintext to be switched to a smaller modulus
475         @param[in] parms_id The target parms_id
476         @param[out] destination The plaintext to overwrite with the modulus switched result
477         @throws std::invalid_argument if plain is not in NTT form
478         @throws std::invalid_argument if plain is not valid for the encryption parameters
479         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
480         @throws std::invalid_argument if plain is already at lower level in modulus chain than the parameters
481         corresponding to parms_id
482         @throws std::invalid_argument if the scale is too large for the new encryption parameters
483         */
mod_switch_to(const Plaintext & plain,parms_id_type parms_id,Plaintext & destination)484         inline void mod_switch_to(const Plaintext &plain, parms_id_type parms_id, Plaintext &destination) const
485         {
486             destination = plain;
487             mod_switch_to_inplace(destination, parms_id);
488         }
489 
490         /**
491         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}, scales
492         the message down accordingly, and stores the result in the destination parameter. Dynamic memory allocations in
493         the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
494 
495         @param[in] encrypted The ciphertext to be switched to a smaller modulus
496         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
497         @param[out] destination The ciphertext to overwrite with the modulus switched result
498         @throws std::invalid_argument if the scheme is invalid for rescaling
499         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
500         @throws std::invalid_argument if encrypted is not in the default NTT form
501         @throws std::invalid_argument if encrypted is already at lowest level
502         @throws std::invalid_argument if pool is uninitialized
503         @throws std::logic_error if result ciphertext is transparent
504         */
505         void rescale_to_next(
506             const Ciphertext &encrypted, Ciphertext &destination,
507             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
508 
509         /**
510         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
511         scales the message down accordingly. Dynamic memory allocations in the process are allocated from the memory
512         pool pointed to by the given MemoryPoolHandle.
513 
514         @param[in] encrypted The ciphertext to be switched to a smaller modulus
515         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
516         @throws std::invalid_argument if the scheme is invalid for rescaling
517         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
518         @throws std::invalid_argument if encrypted is not in the default NTT form
519         @throws std::invalid_argument if encrypted is already at lowest level
520         @throws std::invalid_argument if pool is uninitialized
521         @throws std::logic_error if result ciphertext is transparent
522         */
523         inline void rescale_to_next_inplace(
524             Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const
525         {
526             rescale_to_next(encrypted, encrypted, std::move(pool));
527         }
528 
529         /**
530         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
531         reach the given parms_id and scales the message down accordingly. Dynamic memory allocations in the process are
532         allocated from the memory pool pointed to by the given MemoryPoolHandle.
533 
534         @param[in] encrypted The ciphertext to be switched to a smaller modulus
535         @param[in] parms_id The target parms_id
536         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
537         @throws std::invalid_argument if the scheme is invalid for rescaling
538         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
539         @throws std::invalid_argument if encrypted is not in the default NTT form
540         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
541         @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters
542         corresponding to parms_id
543         @throws std::invalid_argument if pool is uninitialized
544         @throws std::logic_error if result ciphertext is transparent
545         */
546         void rescale_to_inplace(
547             Ciphertext &encrypted, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
548 
549         /**
550         Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
551         reach the given parms_id, scales the message down accordingly, and stores the result in the destination
552         parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
553         MemoryPoolHandle.
554 
555         @param[in] encrypted The ciphertext to be switched to a smaller modulus
556         @param[in] parms_id The target parms_id
557         @param[out] destination The ciphertext to overwrite with the modulus switched result
558         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
559         @throws std::invalid_argument if the scheme is invalid for rescaling
560         @throws std::invalid_argument if encrypted is not valid for the encryption parameters
561         @throws std::invalid_argument if encrypted is not in the default NTT form
562         @throws std::invalid_argument if parms_id is not valid for the encryption parameters
563         @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters
564         corresponding to parms_id
565         @throws std::invalid_argument if pool is uninitialized
566         @throws std::logic_error if result ciphertext is transparent
567         */
568         inline void rescale_to(
569             const Ciphertext &encrypted, parms_id_type parms_id, Ciphertext &destination,
570             MemoryPoolHandle pool = MemoryManager::GetPool()) const
571         {
572             destination = encrypted;
573             rescale_to_inplace(destination, parms_id, std::move(pool));
574         }
575 
576         /**
577         Multiplies several ciphertexts together. This function computes the product of several ciphertext given as an
578         std::vector and stores the result in the destination parameter. The multiplication is done in a depth-optimal
579         order, and relinearization is performed automatically after every multiplication in the process. In
580         relinearization the given relinearization keys are used. Dynamic memory allocations in the process are allocated
581         from the memory pool pointed to by the given MemoryPoolHandle.
582 
583         @param[in] encrypteds The ciphertexts to multiply
584         @param[in] relin_keys The relinearization keys
585         @param[out] destination The ciphertext to overwrite with the multiplication result
586         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
587         @throws std::logic_error if scheme is not scheme_type::bfv
588         @throws std::invalid_argument if encrypteds is empty
589         @throws std::invalid_argument if ciphertexts or relin_keys are not valid for the encryption parameters
590         @throws std::invalid_argument if encrypteds are not in the default NTT form
591         @throws std::invalid_argument if the output scale is too large for the encryption parameters
592         @throws std::invalid_argument if the size of relin_keys is too small
593         @throws std::invalid_argument if pool is uninitialized
594         @throws std::logic_error if keyswitching is not supported by the context
595         @throws std::logic_error if result ciphertext is transparent
596         */
597         void multiply_many(
598             const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys, Ciphertext &destination,
599             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
600 
601         /**
602         Exponentiates a ciphertext. This functions raises encrypted to a power. Dynamic memory allocations in the
603         process are allocated from the memory pool pointed to by the given MemoryPoolHandle. The exponentiation is done
604         in a depth-optimal order, and relinearization is performed automatically after every multiplication in the
605         process. In relinearization the given relinearization keys are used.
606 
607         @param[in] encrypted The ciphertext to exponentiate
608         @param[in] exponent The power to raise the ciphertext to
609         @param[in] relin_keys The relinearization keys
610         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
611         @throws std::logic_error if scheme is not scheme_type::bfv
612         @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters
613         @throws std::invalid_argument if encrypted is not in the default NTT form
614         @throws std::invalid_argument if the output scale is too large for the encryption parameters
615         @throws std::invalid_argument if exponent is zero
616         @throws std::invalid_argument if the size of relin_keys is too small
617         @throws std::invalid_argument if pool is uninitialized
618         @throws std::logic_error if keyswitching is not supported by the context
619         @throws std::logic_error if result ciphertext is transparent
620         */
621         void exponentiate_inplace(
622             Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys,
623             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
624 
625         /**
626         Exponentiates a ciphertext. This functions raises encrypted to a power and stores the result in the destination
627         parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
628         MemoryPoolHandle. The exponentiation is done in a depth-optimal order, and relinearization is performed
629         automatically after every multiplication in the process. In relinearization the given relinearization keys are
630         used.
631 
632         @param[in] encrypted The ciphertext to exponentiate
633         @param[in] exponent The power to raise the ciphertext to
634         @param[in] relin_keys The relinearization keys
635         @param[out] destination The ciphertext to overwrite with the power
636         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
637         @throws std::logic_error if scheme is not scheme_type::bfv
638         @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters
639         @throws std::invalid_argument if encrypted is not in the default NTT form
640         @throws std::invalid_argument if the output scale is too large for the encryption parameters
641         @throws std::invalid_argument if exponent is zero
642         @throws std::invalid_argument if the size of relin_keys is too small
643         @throws std::invalid_argument if pool is uninitialized
644         @throws std::logic_error if keyswitching is not supported by the context
645         @throws std::logic_error if result ciphertext is transparent
646         */
647         inline void exponentiate(
648             const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys, Ciphertext &destination,
649             MemoryPoolHandle pool = MemoryManager::GetPool()) const
650         {
651             destination = encrypted;
652             exponentiate_inplace(destination, exponent, relin_keys, std::move(pool));
653         }
654 
655         /**
656         Adds a ciphertext and a plaintext.
657 
658         @param[in] encrypted The ciphertext to add
659         @param[in] plain The plaintext to add
660         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
661         @throws std::invalid_argument if encrypted or plain is not in the default NTT form
662         @throws std::invalid_argument if encrypted and plain are at different level or scale
663         @throws std::logic_error if result ciphertext is transparent
664         */
665         void add_plain_inplace(Ciphertext &encrypted, const Plaintext &plain) const;
666 
667         /**
668         Adds a ciphertext and a plaintext. This function adds a ciphertext and a plaintext and stores the result in the
669         destination parameter. Note that in many cases it can be much more efficient to perform any computations on raw
670         unencrypted data before encoding it, rather than using this function to compute on the plaintext objects.
671 
672         @param[in] encrypted The ciphertext to add
673         @param[in] plain The plaintext to add
674         @param[out] destination The ciphertext to overwrite with the addition result
675         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
676         @throws std::invalid_argument if encrypted or plain is not in the default NTT form
677         @throws std::invalid_argument if encrypted and plain are at different level or scale
678         @throws std::logic_error if result ciphertext is transparent
679         */
add_plain(const Ciphertext & encrypted,const Plaintext & plain,Ciphertext & destination)680         inline void add_plain(const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination) const
681         {
682             destination = encrypted;
683             add_plain_inplace(destination, plain);
684         }
685 
686         /**
687         Subtracts a plaintext from a ciphertext.
688 
689         @param[in] encrypted The ciphertext to subtract from
690         @param[in] plain The plaintext to subtract
691         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
692         @throws std::invalid_argument if encrypted or plain is not in the default NTT form
693         @throws std::invalid_argument if encrypted and plain are at different level or scale
694         @throws std::logic_error if result ciphertext is transparent
695         */
696         void sub_plain_inplace(Ciphertext &encrypted, const Plaintext &plain) const;
697 
698         /**
699         Subtracts a plaintext from a ciphertext. This function subtracts a plaintext from a ciphertext and stores the
700         result in the destination parameter.
701 
702         @param[in] encrypted The ciphertext to subtract from
703         @param[in] plain The plaintext to subtract
704         @param[out] destination The ciphertext to overwrite with the subtraction result
705         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
706         @throws std::invalid_argument if encrypted or plain is not in the default NTT form
707         @throws std::invalid_argument if encrypted and plain are at different level or scale
708         @throws std::logic_error if result ciphertext is transparent
709         */
sub_plain(const Ciphertext & encrypted,const Plaintext & plain,Ciphertext & destination)710         inline void sub_plain(const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination) const
711         {
712             destination = encrypted;
713             sub_plain_inplace(destination, plain);
714         }
715 
716         /**
717         Multiplies a ciphertext with a plaintext. The plaintext cannot be identically 0. Dynamic memory allocations in
718         the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
719 
720         @param[in] encrypted The ciphertext to multiply
721         @param[in] plain The plaintext to multiply
722         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
723         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
724         @throws std::invalid_argument if encrypted and plain are in different NTT forms
725         @throws std::invalid_argument if the output scale is too large for the encryption parameters
726         @throws std::invalid_argument if pool is uninitialized
727         @throws std::logic_error if result ciphertext is transparent
728         */
729         void multiply_plain_inplace(
730             Ciphertext &encrypted, const Plaintext &plain, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
731 
732         /**
733         Multiplies a ciphertext with a plaintext. This function multiplies a ciphertext with a plaintext and stores the
734         result in the destination parameter. The plaintext cannot be identically 0. Dynamic memory allocations in the
735         process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
736 
737         @param[in] encrypted The ciphertext to multiply
738         @param[in] plain The plaintext to multiply
739         @param[out] destination The ciphertext to overwrite with the multiplication result
740         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
741         @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters
742         @throws std::invalid_argument if encrypted and plain are in different NTT forms
743         @throws std::invalid_argument if the output scale is too large for the encryption parameters
744         @throws std::invalid_argument if pool is uninitialized
745         @throws std::logic_error if result ciphertext is transparent
746         */
747         inline void multiply_plain(
748             const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination,
749             MemoryPoolHandle pool = MemoryManager::GetPool()) const
750         {
751             destination = encrypted;
752             multiply_plain_inplace(destination, plain, std::move(pool));
753         }
754 
755         /**
756         Transforms a plaintext to NTT domain. This functions applies the Number Theoretic Transform to a plaintext by
757         first embedding integers modulo the plaintext modulus to integers modulo the coefficient modulus and then
758         performing David Harvey's NTT on the resulting polynomial. The transformation is done with respect to encryption
759         parameters corresponding to a given parms_id. For the operation to be valid, the plaintext must have degree less
760         than poly_modulus_degree and each coefficient must be less than the plaintext modulus, i.e., the plaintext must
761         be a valid plaintext under the current encryption parameters. Dynamic memory allocations in the process are
762         allocated from the memory pool pointed to by the given MemoryPoolHandle.
763 
764         @param[in] plain The plaintext to transform
765         @param[in] parms_id The parms_id with respect to which the NTT is done
766         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
767         @throws std::invalid_argument if plain is already in NTT form
768         @throws std::invalid_argument if plain or parms_id is not valid for the
769         encryption parameters
770         @throws std::invalid_argument if pool is uninitialized
771         */
772         void transform_to_ntt_inplace(
773             Plaintext &plain, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
774 
775         /**
776         Transforms a plaintext to NTT domain. This functions applies the Number Theoretic Transform to a plaintext by
777         first embedding integers modulo the plaintext modulus to integers modulo the coefficient modulus and then
778         performing David Harvey's NTT on the resulting polynomial. The transformation is done with respect to encryption
779         parameters corresponding to a given parms_id. The result is stored in the destination_ntt parameter. For the
780         operation to be valid, the plaintext must have degree less than poly_modulus_degree and each coefficient must be
781         less than the plaintext modulus, i.e., the plaintext must be a valid plaintext under the current encryption
782         parameters. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
783         MemoryPoolHandle.
784 
785         @param[in] plain The plaintext to transform
786         @param[in] parms_id The parms_id with respect to which the NTT is done
787         @param[out] destinationNTT The plaintext to overwrite with the transformed result
788         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
789         @throws std::invalid_argument if plain is already in NTT form
790         @throws std::invalid_argument if plain or parms_id is not valid for the
791         encryption parameters
792         @throws std::invalid_argument if pool is uninitialized
793         */
794         inline void transform_to_ntt(
795             const Plaintext &plain, parms_id_type parms_id, Plaintext &destination_ntt,
796             MemoryPoolHandle pool = MemoryManager::GetPool()) const
797         {
798             destination_ntt = plain;
799             transform_to_ntt_inplace(destination_ntt, parms_id, std::move(pool));
800         }
801 
802         /**
803         Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform
804         separately to each polynomial of a ciphertext.
805 
806         @param[in] encrypted The ciphertext to transform
807         @throws std::invalid_argument if encrypted is not valid for the encryption
808         parameters
809         @throws std::invalid_argument if encrypted is already in NTT form
810         @throws std::logic_error if result ciphertext is transparent
811         */
812         void transform_to_ntt_inplace(Ciphertext &encrypted) const;
813 
814         /**
815         Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform
816         separately to each polynomial of a ciphertext. The result is stored in the destination_ntt parameter.
817 
818         @param[in] encrypted The ciphertext to transform
819         @param[out] destination_ntt The ciphertext to overwrite with the transformed result
820         @throws std::invalid_argument if encrypted is not valid for the encryption
821         parameters
822         @throws std::invalid_argument if encrypted is already in NTT form
823         @throws std::logic_error if result ciphertext is transparent
824         */
transform_to_ntt(const Ciphertext & encrypted,Ciphertext & destination_ntt)825         inline void transform_to_ntt(const Ciphertext &encrypted, Ciphertext &destination_ntt) const
826         {
827             destination_ntt = encrypted;
828             transform_to_ntt_inplace(destination_ntt);
829         }
830 
831         /**
832         Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number
833         Theoretic Transform separately to each polynomial of a ciphertext.
834 
835         @param[in] encrypted_ntt The ciphertext to transform
836         @throws std::invalid_argument if encrypted_ntt is not valid for the encryption
837         parameters
838         @throws std::invalid_argument if encrypted_ntt is not in NTT form
839         @throws std::logic_error if result ciphertext is transparent
840         */
841         void transform_from_ntt_inplace(Ciphertext &encrypted_ntt) const;
842 
843         /**
844         Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number
845         Theoretic Transform separately to each polynomial of a ciphertext. The result is stored in the destination
846         parameter.
847 
848         @param[in] encrypted_ntt The ciphertext to transform
849         @param[out] destination The ciphertext to overwrite with the transformed result
850         @throws std::invalid_argument if encrypted_ntt is not valid for the encryption
851         parameters
852         @throws std::invalid_argument if encrypted_ntt is not in NTT form
853         @throws std::logic_error if result ciphertext is transparent
854         */
transform_from_ntt(const Ciphertext & encrypted_ntt,Ciphertext & destination)855         inline void transform_from_ntt(const Ciphertext &encrypted_ntt, Ciphertext &destination) const
856         {
857             destination = encrypted_ntt;
858             transform_from_ntt_inplace(destination);
859         }
860 
861         /**
862         Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, an appropriate set of Galois
863         keys must also be provided. Dynamic memory allocations in the process are allocated from the memory pool pointed
864         to by the given MemoryPoolHandle.
865 
866         The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval
867         [1, M-1], where M = 2*N, and N = poly_modulus_degree. Used with batching, a Galois element 3^i % M corresponds
868         to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row
869         rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV, and
870         complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois element p
871         changes Enc(plain(x)) to Enc(plain(x^p)).
872 
873         @param[in] encrypted The ciphertext to apply the Galois automorphism to
874         @param[in] galois_elt The Galois element
875         @param[in] galois_keys The Galois keys
876         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
877         @throws std::invalid_argument if encrypted or galois_keys is not valid for
878         the encryption parameters
879         @throws std::invalid_argument if galois_keys do not correspond to the top
880         level parameters in the current context
881         @throws std::invalid_argument if encrypted is not in the default NTT form
882         @throws std::invalid_argument if encrypted has size larger than 2
883         @throws std::invalid_argument if the Galois element is not valid
884         @throws std::invalid_argument if necessary Galois keys are not present
885         @throws std::invalid_argument if pool is uninitialized
886         @throws std::logic_error if keyswitching is not supported by the context
887         @throws std::logic_error if result ciphertext is transparent
888         */
889         void apply_galois_inplace(
890             Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys,
891             MemoryPoolHandle pool = MemoryManager::GetPool()) const;
892 
893         /**
894         Applies a Galois automorphism to a ciphertext and writes the result to the destination parameter. To evaluate
895         the Galois automorphism, an appropriate set of Galois keys must also be provided. Dynamic memory allocations in
896         the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
897 
898         The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval
899         [1, M-1], where M = 2*N, and N = poly_modulus_degree. Used with batching, a Galois element 3^i % M corresponds
900         to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row
901         rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV, and
902         complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois element p
903         changes Enc(plain(x)) to Enc(plain(x^p)).
904 
905         @param[in] encrypted The ciphertext to apply the Galois automorphism to
906         @param[in] galois_elt The Galois element
907         @param[in] galois_keys The Galois keys
908         @param[out] destination The ciphertext to overwrite with the result
909         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
910         @throws std::invalid_argument if encrypted or galois_keys is not valid for
911         the encryption parameters
912         @throws std::invalid_argument if galois_keys do not correspond to the top
913         level parameters in the current context
914         @throws std::invalid_argument if encrypted is not in the default NTT form
915         @throws std::invalid_argument if encrypted has size larger than 2
916         @throws std::invalid_argument if the Galois element is not valid
917         @throws std::invalid_argument if necessary Galois keys are not present
918         @throws std::invalid_argument if pool is uninitialized
919         @throws std::logic_error if keyswitching is not supported by the context
920         @throws std::logic_error if result ciphertext is transparent
921         */
922         inline void apply_galois(
923             const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys,
924             Ciphertext &destination, MemoryPoolHandle pool = MemoryManager::GetPool()) const
925         {
926             destination = encrypted;
927             apply_galois_inplace(destination, galois_elt, galois_keys, std::move(pool));
928         }
929 
930         /**
931         Rotates plaintext matrix rows cyclically. When batching is used with the BFV scheme, this function rotates the
932         encrypted plaintext matrix rows cyclically to the left (steps > 0) or to the right (steps < 0). Since the size
933         of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, the number of steps to
934         rotate must have absolute value at most N/2-1. Dynamic memory allocations in the process are allocated from the
935         memory pool pointed to by the given MemoryPoolHandle.
936 
937         @param[in] encrypted The ciphertext to rotate
938         @param[in] steps The number of steps to rotate (positive left, negative right)
939         @param[in] galois_keys The Galois keys
940         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
941         @throws std::logic_error if scheme is not scheme_type::bfv
942         @throws std::logic_error if the encryption parameters do not support batching
943         @throws std::invalid_argument if encrypted or galois_keys is not valid for
944         the encryption parameters
945         @throws std::invalid_argument if galois_keys do not correspond to the top
946         level parameters in the current context
947         @throws std::invalid_argument if encrypted is not in the default NTT form
948         @throws std::invalid_argument if encrypted has size larger than 2
949         @throws std::invalid_argument if steps has too big absolute value
950         @throws std::invalid_argument if necessary Galois keys are not present
951         @throws std::invalid_argument if pool is uninitialized
952         @throws std::logic_error if keyswitching is not supported by the context
953         @throws std::logic_error if result ciphertext is transparent
954         */
955         inline void rotate_rows_inplace(
956             Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys,
957             MemoryPoolHandle pool = MemoryManager::GetPool()) const
958         {
959             if (context_.key_context_data()->parms().scheme() != scheme_type::bfv)
960             {
961                 throw std::logic_error("unsupported scheme");
962             }
963             rotate_internal(encrypted, steps, galois_keys, std::move(pool));
964         }
965 
966         /**
967         Rotates plaintext matrix rows cyclically. When batching is used with the BFV scheme, this function rotates the
968         encrypted plaintext matrix rows cyclically to the left (steps > 0) or to the right (steps < 0) and writes the
969         result to the destination parameter. Since the size of the batched matrix is 2-by-(N/2), where N is the degree
970         of the polynomial modulus, the number of steps to rotate must have absolute value at most N/2-1. Dynamic memory
971         allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
972 
973         @param[in] encrypted The ciphertext to rotate
974         @param[in] steps The number of steps to rotate (positive left, negative right)
975         @param[in] galois_keys The Galois keys
976         @param[out] destination The ciphertext to overwrite with the rotated result
977         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
978         @throws std::logic_error if scheme is not scheme_type::bfv
979         @throws std::logic_error if the encryption parameters do not support batching
980         @throws std::invalid_argument if encrypted or galois_keys is not valid for
981         the encryption parameters
982         @throws std::invalid_argument if galois_keys do not correspond to the top
983         level parameters in the current context
984         @throws std::invalid_argument if encrypted is in NTT form
985         @throws std::invalid_argument if encrypted has size larger than 2
986         @throws std::invalid_argument if steps has too big absolute value
987         @throws std::invalid_argument if necessary Galois keys are not present
988         @throws std::invalid_argument if pool is uninitialized
989         @throws std::logic_error if keyswitching is not supported by the context
990         @throws std::logic_error if result ciphertext is transparent
991         */
992         inline void rotate_rows(
993             const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, Ciphertext &destination,
994             MemoryPoolHandle pool = MemoryManager::GetPool()) const
995         {
996             destination = encrypted;
997             rotate_rows_inplace(destination, steps, galois_keys, std::move(pool));
998         }
999 
1000         /**
1001         Rotates plaintext matrix columns cyclically. When batching is used with the BFV scheme, this function rotates
1002         the encrypted plaintext matrix columns cyclically. Since the size of the batched matrix is 2-by-(N/2), where N
1003         is the degree of the polynomial modulus, this means simply swapping the two rows. Dynamic memory allocations in
1004         the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
1005 
1006         @param[in] encrypted The ciphertext to rotate
1007         @param[in] galois_keys The Galois keys
1008         @param[out] destination The ciphertext to overwrite with the rotated result
1009         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1010         @throws std::logic_error if scheme is not scheme_type::bfv
1011         @throws std::logic_error if the encryption parameters do not support batching
1012         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1013         the encryption parameters
1014         @throws std::invalid_argument if galois_keys do not correspond to the top
1015         level parameters in the current context
1016         @throws std::invalid_argument if encrypted is in NTT form
1017         @throws std::invalid_argument if encrypted has size larger than 2
1018         @throws std::invalid_argument if necessary Galois keys are not present
1019         @throws std::invalid_argument if pool is uninitialized
1020         @throws std::logic_error if keyswitching is not supported by the context
1021         @throws std::logic_error if result ciphertext is transparent
1022         */
1023         inline void rotate_columns_inplace(
1024             Ciphertext &encrypted, const GaloisKeys &galois_keys,
1025             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1026         {
1027             if (context_.key_context_data()->parms().scheme() != scheme_type::bfv)
1028             {
1029                 throw std::logic_error("unsupported scheme");
1030             }
1031             conjugate_internal(encrypted, galois_keys, std::move(pool));
1032         }
1033 
1034         /**
1035         Rotates plaintext matrix columns cyclically. When batching is used with the BFV scheme, this function rotates
1036         the encrypted plaintext matrix columns cyclically, and writes the result to the destination parameter. Since the
1037         size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, this means simply
1038         swapping the two rows. Dynamic memory allocations in the process are allocated from the memory pool pointed to
1039         by the given MemoryPoolHandle.
1040 
1041         @param[in] encrypted The ciphertext to rotate
1042         @param[in] galois_keys The Galois keys
1043         @param[out] destination The ciphertext to overwrite with the rotated result
1044         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1045         @throws std::logic_error if scheme is not scheme_type::bfv
1046         @throws std::logic_error if the encryption parameters do not support batching
1047         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1048         the encryption parameters
1049         @throws std::invalid_argument if galois_keys do not correspond to the top
1050         level parameters in the current context
1051         @throws std::invalid_argument if encrypted is in NTT form
1052         @throws std::invalid_argument if encrypted has size larger than 2
1053         @throws std::invalid_argument if necessary Galois keys are not present
1054         @throws std::invalid_argument if pool is uninitialized
1055         @throws std::logic_error if keyswitching is not supported by the context
1056         @throws std::logic_error if result ciphertext is transparent
1057         */
1058         inline void rotate_columns(
1059             const Ciphertext &encrypted, const GaloisKeys &galois_keys, Ciphertext &destination,
1060             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1061         {
1062             destination = encrypted;
1063             rotate_columns_inplace(destination, galois_keys, std::move(pool));
1064         }
1065 
1066         /**
1067         Rotates plaintext vector cyclically. When using the CKKS scheme, this function rotates the encrypted plaintext
1068         vector cyclically to the left (steps > 0) or to the right (steps < 0). Since the size of the batched matrix is
1069         2-by-(N/2), where N is the degree of the polynomial modulus, the number of steps to rotate must have absolute
1070         value at most N/2-1. Dynamic memory allocations in the process are allocated from the memory pool pointed to by
1071         the given MemoryPoolHandle.
1072 
1073         @param[in] encrypted The ciphertext to rotate
1074         @param[in] steps The number of steps to rotate (positive left, negative right)
1075         @param[in] galois_keys The Galois keys
1076         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1077         @throws std::logic_error if scheme is not scheme_type::ckks
1078         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1079         the encryption parameters
1080         @throws std::invalid_argument if galois_keys do not correspond to the top
1081         level parameters in the current context
1082         @throws std::invalid_argument if encrypted is not in the default NTT form
1083         @throws std::invalid_argument if encrypted has size larger than 2
1084         @throws std::invalid_argument if steps has too big absolute value
1085         @throws std::invalid_argument if necessary Galois keys are not present
1086         @throws std::invalid_argument if pool is uninitialized
1087         @throws std::logic_error if keyswitching is not supported by the context
1088         @throws std::logic_error if result ciphertext is transparent
1089         */
1090         inline void rotate_vector_inplace(
1091             Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys,
1092             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1093         {
1094             if (context_.key_context_data()->parms().scheme() != scheme_type::ckks)
1095             {
1096                 throw std::logic_error("unsupported scheme");
1097             }
1098             rotate_internal(encrypted, steps, galois_keys, std::move(pool));
1099         }
1100 
1101         /**
1102         Rotates plaintext vector cyclically. When using the CKKS scheme, this function rotates the encrypted plaintext
1103         vector cyclically to the left (steps > 0) or to the right (steps < 0) and writes the result to the destination
1104         parameter. Since the size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus,
1105         the number of steps to rotate must have absolute value at most N/2-1. Dynamic memory allocations in the process
1106         are allocated from the memory pool pointed to by the given MemoryPoolHandle.
1107 
1108         @param[in] encrypted The ciphertext to rotate
1109         @param[in] steps The number of steps to rotate (positive left, negative right)
1110         @param[in] galois_keys The Galois keys
1111         @param[out] destination The ciphertext to overwrite with the rotated result
1112         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1113         @throws std::logic_error if scheme is not scheme_type::ckks
1114         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1115         the encryption parameters
1116         @throws std::invalid_argument if galois_keys do not correspond to the top
1117         level parameters in the current context
1118         @throws std::invalid_argument if encrypted is in NTT form
1119         @throws std::invalid_argument if encrypted has size larger than 2
1120         @throws std::invalid_argument if steps has too big absolute value
1121         @throws std::invalid_argument if necessary Galois keys are not present
1122         @throws std::invalid_argument if pool is uninitialized
1123         @throws std::logic_error if keyswitching is not supported by the context
1124         @throws std::logic_error if result ciphertext is transparent
1125         */
1126         inline void rotate_vector(
1127             const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, Ciphertext &destination,
1128             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1129         {
1130             destination = encrypted;
1131             rotate_vector_inplace(destination, steps, galois_keys, std::move(pool));
1132         }
1133 
1134         /**
1135         Complex conjugates plaintext slot values. When using the CKKS scheme, this function complex conjugates all
1136         values in the underlying plaintext. Dynamic memory allocations in the process are allocated from the memory pool
1137         pointed to by the given MemoryPoolHandle.
1138 
1139         @param[in] encrypted The ciphertext to rotate
1140         @param[in] galois_keys The Galois keys
1141         @param[out] destination The ciphertext to overwrite with the rotated result
1142         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1143         @throws std::logic_error if scheme is not scheme_type::ckks
1144         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1145         the encryption parameters
1146         @throws std::invalid_argument if galois_keys do not correspond to the top
1147         level parameters in the current context
1148         @throws std::invalid_argument if encrypted is in NTT form
1149         @throws std::invalid_argument if encrypted has size larger than 2
1150         @throws std::invalid_argument if necessary Galois keys are not present
1151         @throws std::invalid_argument if pool is uninitialized
1152         @throws std::logic_error if keyswitching is not supported by the context
1153         @throws std::logic_error if result ciphertext is transparent
1154         */
1155         inline void complex_conjugate_inplace(
1156             Ciphertext &encrypted, const GaloisKeys &galois_keys,
1157             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1158         {
1159             if (context_.key_context_data()->parms().scheme() != scheme_type::ckks)
1160             {
1161                 throw std::logic_error("unsupported scheme");
1162             }
1163             conjugate_internal(encrypted, galois_keys, std::move(pool));
1164         }
1165 
1166         /**
1167         Complex conjugates plaintext slot values. When using the CKKS scheme, this function complex conjugates all
1168         values in the underlying plaintext, and writes the result to the destination parameter. Dynamic memory
1169         allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
1170 
1171         @param[in] encrypted The ciphertext to rotate
1172         @param[in] galois_keys The Galois keys
1173         @param[out] destination The ciphertext to overwrite with the rotated result
1174         @param[in] pool The MemoryPoolHandle pointing to a valid memory pool
1175         @throws std::logic_error if scheme is not scheme_type::ckks
1176         @throws std::invalid_argument if encrypted or galois_keys is not valid for
1177         the encryption parameters
1178         @throws std::invalid_argument if galois_keys do not correspond to the top
1179         level parameters in the current context
1180         @throws std::invalid_argument if encrypted is in NTT form
1181         @throws std::invalid_argument if encrypted has size larger than 2
1182         @throws std::invalid_argument if necessary Galois keys are not present
1183         @throws std::invalid_argument if pool is uninitialized
1184         @throws std::logic_error if keyswitching is not supported by the context
1185         @throws std::logic_error if result ciphertext is transparent
1186         */
1187         inline void complex_conjugate(
1188             const Ciphertext &encrypted, const GaloisKeys &galois_keys, Ciphertext &destination,
1189             MemoryPoolHandle pool = MemoryManager::GetPool()) const
1190         {
1191             destination = encrypted;
1192             complex_conjugate_inplace(destination, galois_keys, std::move(pool));
1193         }
1194 
1195         /**
1196         Enables access to private members of seal::Evaluator for SEAL_C.
1197         */
1198         struct EvaluatorPrivateHelper;
1199 
1200     private:
1201         Evaluator(const Evaluator &copy) = delete;
1202 
1203         Evaluator(Evaluator &&source) = delete;
1204 
1205         Evaluator &operator=(const Evaluator &assign) = delete;
1206 
1207         Evaluator &operator=(Evaluator &&assign) = delete;
1208 
1209         void bfv_multiply(Ciphertext &encrypted1, const Ciphertext &encrypted2, MemoryPoolHandle pool) const;
1210 
1211         void ckks_multiply(Ciphertext &encrypted1, const Ciphertext &encrypted2, MemoryPoolHandle pool) const;
1212 
1213         void bfv_square(Ciphertext &encrypted, MemoryPoolHandle pool) const;
1214 
1215         void ckks_square(Ciphertext &encrypted, MemoryPoolHandle pool) const;
1216 
1217         void relinearize_internal(
1218             Ciphertext &encrypted, const RelinKeys &relin_keys, std::size_t destination_size,
1219             MemoryPoolHandle pool) const;
1220 
1221         void mod_switch_scale_to_next(
1222             const Ciphertext &encrypted, Ciphertext &destination, MemoryPoolHandle pool) const;
1223 
1224         void mod_switch_drop_to_next(const Ciphertext &encrypted, Ciphertext &destination, MemoryPoolHandle pool) const;
1225 
1226         void mod_switch_drop_to_next(Plaintext &plain) const;
1227 
1228         void rotate_internal(
1229             Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, MemoryPoolHandle pool) const;
1230 
conjugate_internal(Ciphertext & encrypted,const GaloisKeys & galois_keys,MemoryPoolHandle pool)1231         inline void conjugate_internal(
1232             Ciphertext &encrypted, const GaloisKeys &galois_keys, MemoryPoolHandle pool) const
1233         {
1234             // Verify parameters.
1235             auto context_data_ptr = context_.get_context_data(encrypted.parms_id());
1236             if (!context_data_ptr)
1237             {
1238                 throw std::invalid_argument("encrypted is not valid for encryption parameters");
1239             }
1240 
1241             // Extract encryption parameters.
1242             auto &context_data = *context_data_ptr;
1243             if (!context_data.qualifiers().using_batching)
1244             {
1245                 throw std::logic_error("encryption parameters do not support batching");
1246             }
1247 
1248             auto galois_tool = context_data.galois_tool();
1249 
1250             // Perform rotation and key switching
1251             apply_galois_inplace(encrypted, galois_tool->get_elt_from_step(0), galois_keys, std::move(pool));
1252         }
1253 
1254         void switch_key_inplace(
1255             Ciphertext &encrypted, util::ConstRNSIter target_iter, const KSwitchKeys &kswitch_keys,
1256             std::size_t key_index, MemoryPoolHandle pool = MemoryManager::GetPool()) const;
1257 
1258         void multiply_plain_normal(Ciphertext &encrypted, const Plaintext &plain, MemoryPoolHandle pool) const;
1259 
1260         void multiply_plain_ntt(Ciphertext &encrypted_ntt, const Plaintext &plain_ntt) const;
1261 
1262         SEALContext context_;
1263     };
1264 } // namespace seal
1265