1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT license.
3 
4 using Microsoft.Research.SEAL.Tools;
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Runtime.InteropServices;
9 
10 namespace Microsoft.Research.SEAL
11 {
12     /// <summary>
13     /// Provides operations on ciphertexts.
14     /// </summary>
15     ///
16     /// <remarks>
17     /// <para>
18     /// Provides operations on ciphertexts. Due to the properties of the encryption scheme, the arithmetic operations
19     /// pass through the encryption layer to the underlying plaintext, changing it according to the type of the
20     /// operation. Since the plaintext elements are fundamentally polynomials in the polynomial quotient ring
21     /// Z_T[x]/(X^N+1), where T is the plaintext modulus and X^N+1 is the polynomial modulus, this is the ring where
22     /// the arithmetic operations will take place. BatchEncoder (batching) provider an alternative possibly more
23     /// convenient view of the plaintext elements as 2-by-(N2/2) matrices of integers modulo the plaintext modulus. In
24     /// the batching view the arithmetic operations act on the matrices element-wise. Some of the operations only apply
25     /// in the batching view, such as matrix row and column rotations. Other operations such as relinearization have no
26     /// semantic meaning but are necessary for performance reasons.
27     /// </para>
28     /// <para>
29     /// Arithmetic Operations
30     /// The core operations are arithmetic operations, in particular multiplication and addition of ciphertexts. In
31     /// addition to these, we also provide negation, subtraction, squaring, exponentiation, and multiplication and
32     /// addition of several ciphertexts for convenience. in many cases some of the inputs to a computation are plaintext
33     /// elements rather than ciphertexts. For this we provide fast "plain" operations: plain addition, plain
34     /// subtraction, and plain multiplication.
35     /// </para>
36     /// <para>
37     /// Relinearization
38     /// One of the most important non-arithmetic operations is relinearization, which takes as input a ciphertext of
39     /// size K+1 and relinearization keys (at least K-1 keys are needed), and changes the size of the ciphertext down
40     /// to 2 (minimum size). For most use-cases only one relinearization key suffices, in which case relinearization
41     /// should be performed after every multiplication. Homomorphic multiplication of ciphertexts of size K+1 and L+1
42     /// outputs a ciphertext of size K+L+1, and the computational cost of multiplication is proportional to K*L. Plain
43     /// multiplication and addition operations of any type do not change the size. Relinearization requires
44     /// relinearization keys to have been generated.
45     /// </para>
46     /// <para>
47     /// Rotations
48     /// When batching is enabled, we provide operations for rotating the plaintext matrix rows cyclically left or right,
49     /// and for rotating the columns (swapping the rows). Rotations require Galois keys to have been generated.
50     /// </para>
51     /// <para>
52     /// Other Operations
53     /// We also provide operations for transforming ciphertexts to NTT form and back, and for transforming plaintext
54     /// polynomials to NTT form. These can be used in a very fast plain multiplication variant, that assumes the inputs
55     /// to be in NTT form. Since the NTT has to be done in any case in plain multiplication, this function can be used
56     /// when e.g. one plaintext input is used in several plain multiplication, and transforming it several times would
57     /// not make sense.
58     /// </para>
59     /// <para>
60     /// NTT form
61     /// When using the BFV scheme (SchemeType.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 (SchemeType.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
66     /// input(s), with the exception of the TransformToNTT and TransformFromNTT functions, which change the state.
67     /// Ideally, unless these two functions are called, all other functions should "just work".
68     /// </para>
69     /// </remarks>
70     /// <see cref="EncryptionParameters"/> for more details on encryption parameters.
71     /// <see cref="BatchEncoder"/> for more details on batching
72     /// <see cref="RelinKeys"/> for more details on relinearization keys.
73     /// <see cref="GaloisKeys"/> for more details on Galois keys.
74     public class Evaluator : NativeObject
75     {
76         /// <summary>
77         /// Creates an Evaluator instance initialized with the specified SEALContext.
78         /// </summary>
79         /// <param name="context">The SEALContext</param>
80         /// <exception cref="ArgumentNullException">if context is null</exception>
81         /// <exception cref="ArgumentException">if the encryption parameters are not valid</exception>
Evaluator(SEALContext context)82         public Evaluator(SEALContext context)
83         {
84             if (null == context)
85                 throw new ArgumentNullException(nameof(context));
86             if (!context.ParametersSet)
87                 throw new ArgumentException("Encryption parameters are not set correctly");
88 
89             NativeMethods.Evaluator_Create(context.NativePtr, out IntPtr ptr);
90             NativePtr = ptr;
91         }
92 
93         /// <summary>
94         /// Negates a ciphertext.
95         /// </summary>
96         /// <param name="encrypted">The ciphertext to negate</param>
97         /// <exception cref="ArgumentNullException">if encrypted is null</exception>
98         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
99         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
NegateInplace(Ciphertext encrypted)100         public void NegateInplace(Ciphertext encrypted)
101         {
102             Negate(encrypted, destination: encrypted);
103         }
104 
105         /// <summary>
106         /// Negates a ciphertext and stores the result in the destination parameter.
107         /// </summary>
108         /// <param name="encrypted">The ciphertext to negate</param>
109         /// <param name="destination">The ciphertext to overwrite with the negated result</param>
110         /// <exception cref="ArgumentNullException">if encrypted or destination is null</exception>
111         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
112         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Negate(Ciphertext encrypted, Ciphertext destination)113         public void Negate(Ciphertext encrypted, Ciphertext destination)
114         {
115             if (null == encrypted)
116                 throw new ArgumentNullException(nameof(encrypted));
117             if (null == destination)
118                 throw new ArgumentNullException(nameof(destination));
119 
120             NativeMethods.Evaluator_Negate(NativePtr, encrypted.NativePtr, destination.NativePtr);
121         }
122 
123         /// <summary>
124         /// Adds two ciphertexts.
125         /// </summary>
126         /// <remarks>
127         /// This function adds together encrypted1 and encrypted2 and stores the result in encrypted1.
128         /// </remarks>
129         /// <param name="encrypted1">The first ciphertext to add</param>
130         /// <param name="encrypted2">The second ciphertext to add</param>
131         /// <exception cref="ArgumentNullException">if encrypted1 or encrypted2 is null</exception>
132         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not valid for the encryption
133         /// parameters</exception>
134         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are in different NTT forms</exception>
135         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level or scale</exception>
136         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
AddInplace(Ciphertext encrypted1, Ciphertext encrypted2)137         public void AddInplace(Ciphertext encrypted1, Ciphertext encrypted2)
138         {
139             Add(encrypted1, encrypted2, destination: encrypted1);
140         }
141 
142         /// <summary>
143         /// Adds two ciphertexts.
144         /// </summary>
145         /// <remarks>
146         /// This function adds together encrypted1 and encrypted2 and stores the result in the destination parameter.
147         /// </remarks>
148         /// <param name="encrypted1">The first ciphertext to add</param>
149         /// <param name="encrypted2">The second ciphertext to add</param>
150         /// <param name="destination">The ciphertext to overwrite with the addition result</param>
151         /// <exception cref="ArgumentNullException">if encrypted1, encrypted2, or destination is null</exception>
152         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not valid for the encryption
153         /// parameters</exception>
154         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are in different NTT forms</exception>
155         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level or scale</exception>
156         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Add(Ciphertext encrypted1, Ciphertext encrypted2, Ciphertext destination)157         public void Add(Ciphertext encrypted1, Ciphertext encrypted2, Ciphertext destination)
158         {
159             if (null == encrypted1)
160                 throw new ArgumentNullException(nameof(encrypted1));
161             if (null == encrypted2)
162                 throw new ArgumentNullException(nameof(encrypted2));
163             if (null == destination)
164                 throw new ArgumentNullException(nameof(destination));
165 
166             NativeMethods.Evaluator_Add(NativePtr, encrypted1.NativePtr, encrypted2.NativePtr, destination.NativePtr);
167         }
168 
169         /// <summary>
170         /// Adds together a vector of ciphertexts and stores the result in the destination parameter.
171         /// </summary>
172         /// <param name="encrypteds">The ciphertexts to add</param>
173         /// <param name="destination">The ciphertext to overwrite with the addition result</param>
174         /// <exception cref="ArgumentNullException">if encrypteds or destination is null</exception>
175         /// <exception cref="ArgumentException">if encrypteds is empty</exception>
176         /// <exception cref="ArgumentException">if encrypteds are not valid for the encryption parameters</exception>
177         /// <exception cref="ArgumentException">if encrypteds are in different NTT forms</exception>
178         /// <exception cref="ArgumentException">if encrypteds are at different level or scale</exception>
179         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
AddMany(IEnumerable<Ciphertext> encrypteds, Ciphertext destination)180         public void AddMany(IEnumerable<Ciphertext> encrypteds, Ciphertext destination)
181         {
182             if (null == encrypteds)
183                 throw new ArgumentNullException(nameof(encrypteds));
184             if (null == destination)
185                 throw new ArgumentNullException(nameof(destination));
186 
187             IntPtr[] encarray = encrypteds.Select(c => c.NativePtr).ToArray();
188             NativeMethods.Evaluator_AddMany(NativePtr, (ulong)encarray.Length, encarray, destination.NativePtr);
189         }
190 
191         /// <summary>
192         /// Subtracts two ciphertexts.
193         /// </summary>
194         /// <remarks>
195         /// This function computes the difference of encrypted1 and encrypted2, and stores the result in encrypted1.
196         /// </remarks>
197         /// <param name="encrypted1">The ciphertext to subtract from</param>
198         /// <param name="encrypted2">The ciphertext to subtract</param>
199         /// <exception cref="ArgumentNullException">if encrypted1 or encrypted2 is null</exception>
200         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not valid for the encryption
201         /// parameters</exception>
202         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are in different NTT forms</exception>
203         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level or scale</exception>
204         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
SubInplace(Ciphertext encrypted1, Ciphertext encrypted2)205         public void SubInplace(Ciphertext encrypted1, Ciphertext encrypted2)
206         {
207             Sub(encrypted1, encrypted2, destination: encrypted1);
208         }
209 
210         /// <summary>
211         /// Subtracts two ciphertexts.
212         /// </summary>
213         /// <remarks>This function computes the difference of encrypted1 and encrypted2 and stores the result in the
214         /// destination parameter.
215         /// </remarks>
216         /// <param name="encrypted1">The ciphertext to subtract from</param>
217         /// <param name="encrypted2">The ciphertext to subtract</param>
218         /// <param name="destination">The ciphertext to overwrite with the subtraction result</param>
219         /// <exception cref="ArgumentNullException">if encrypted1, encrypted2, or destination is null</exception>
220         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not valid for the encryption
221         /// parameters</exception>
222         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are in different NTT forms</exception>
223         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level or scale</exception>
224         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Sub(Ciphertext encrypted1, Ciphertext encrypted2, Ciphertext destination)225         public void Sub(Ciphertext encrypted1, Ciphertext encrypted2, Ciphertext destination)
226         {
227             if (null == encrypted1)
228                 throw new ArgumentNullException(nameof(encrypted1));
229             if (null == encrypted2)
230                 throw new ArgumentNullException(nameof(encrypted2));
231             if (null == destination)
232                 throw new ArgumentNullException(nameof(destination));
233 
234             NativeMethods.Evaluator_Sub(NativePtr, encrypted1.NativePtr, encrypted2.NativePtr, destination.NativePtr);
235         }
236 
237         /// <summary>
238         /// Multiplies two ciphertexts.
239         /// </summary>
240         /// <remarks>
241         /// This functions computes the product of encrypted1 and encrypted2 and stores the result in encrypted1.
242         /// Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
243         /// MemoryPoolHandle.
244         /// </remarks>
245         /// <param name="encrypted1">The first ciphertext to multiply</param>
246         /// <param name="encrypted2">The second ciphertext to multiply</param>
247         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
248         /// <exception cref="ArgumentNullException">if encrypted1 or encrypted2 is null</exception>
249         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not valid for the encryption
250         /// parameters</exception>
251         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not in the default NTT form</exception>
252         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level</exception>
253         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
254         /// parameters</exception>
255         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
256         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
MultiplyInplace(Ciphertext encrypted1, Ciphertext encrypted2, MemoryPoolHandle pool = null)257         public void MultiplyInplace(Ciphertext encrypted1, Ciphertext encrypted2,
258             MemoryPoolHandle pool = null)
259         {
260             Multiply(encrypted1, encrypted2, destination: encrypted1, pool: pool);
261         }
262 
263         /// <summary>
264         /// Multiplies two ciphertexts.
265         /// </summary>
266         /// <remarks>
267         /// This functions computes the product of encrypted1 and encrypted2 and stores the result in the destination
268         /// parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the
269         /// given MemoryPoolHandle.
270         /// </remarks>
271         /// <param name="encrypted1">The first ciphertext to multiply</param>
272         /// <param name="encrypted2">The second ciphertext to multiply</param>
273         /// <param name="destination">The ciphertext to overwrite with the multiplication result</param>
274         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
275         /// <exception cref="ArgumentNullException">if encrypted1, encrypted2, destination is null</exception>
276         /// <exception cref="ArgumentException">if encrypted1 or encrypted2 is not in the default NTT form</exception>
277         /// <exception cref="ArgumentException">if encrypted1 and encrypted2 are at different level</exception>
278         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
279         /// parameters</exception>
280         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
281         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Multiply(Ciphertext encrypted1, Ciphertext encrypted2, Ciphertext destination, MemoryPoolHandle pool = null)282         public void Multiply(Ciphertext encrypted1, Ciphertext encrypted2,
283             Ciphertext destination, MemoryPoolHandle pool = null)
284         {
285             if (null == encrypted1)
286                 throw new ArgumentNullException(nameof(encrypted1));
287             if (null == encrypted2)
288                 throw new ArgumentNullException(nameof(encrypted2));
289             if (null == destination)
290                 throw new ArgumentNullException(nameof(destination));
291 
292             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
293             NativeMethods.Evaluator_Multiply(NativePtr, encrypted1.NativePtr, encrypted2.NativePtr, destination.NativePtr, poolPtr);
294         }
295 
296         /// <summary>
297         /// Squares a ciphertext.
298         /// </summary>
299         /// <remarks>
300         /// This functions computes the square of encrypted. Dynamic memory allocations in the process are allocated
301         /// from the memory pool pointed to by the given MemoryPoolHandle.
302         /// </remarks>
303         /// <param name="encrypted">The ciphertext to square</param>
304         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
305         /// <exception cref="ArgumentNullException">if encrypted is null</exception>
306         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
307         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
308         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
309         /// parameters</exception>
310         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
311         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
SquareInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)312         public void SquareInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)
313         {
314             Square(encrypted, destination: encrypted, pool: pool);
315         }
316 
317         /// <summary>
318         /// Squares a ciphertext.
319         /// </summary>
320         /// <remarks>
321         /// This functions computes the square of encrypted and stores the result in the destination parameter. Dynamic
322         /// memory allocations in the process are allocated from the memory pool pointed to by the given
323         /// MemoryPoolHandle.
324         /// </remarks>
325         /// <param name="encrypted">The ciphertext to square</param>
326         /// <param name="destination">The ciphertext to overwrite with the square</param>
327         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
328         /// <exception cref="ArgumentNullException">if encrypted, destination is null</exception>
329         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
330         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
331         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
332         /// parameters</exception>
333         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
334         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Square(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)335         public void Square(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)
336         {
337             if (null == encrypted)
338                 throw new ArgumentNullException(nameof(encrypted));
339             if (null == destination)
340                 throw new ArgumentNullException(nameof(destination));
341 
342             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
343             NativeMethods.Evaluator_Square(NativePtr, encrypted.NativePtr, destination.NativePtr, poolPtr);
344         }
345 
346         /// <summary>
347         /// Relinearizes a ciphertext.
348         /// </summary>
349         /// <remarks>
350         /// This functions relinearizes encrypted, reducing its size down to 2. If the size of encrypted is K+1, the
351         /// given relinearization keys need to have size at least K-1. Dynamic memory allocations in the process are
352         /// allocated from the memory pool pointed to by the given MemoryPoolHandle.
353         /// </remarks>
354         /// <param name="encrypted">The ciphertext to relinearize</param>
355         /// <param name="relinKeys">The relinearization keys</param>
356         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
357         /// <exception cref="ArgumentNullException">if encrypted or relinKeys is null</exception>
358         /// <exception cref="ArgumentException">if encrypted or relinKeys is not valid for the encryption
359         /// parameters</exception>
360         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
361         /// <exception cref="ArgumentException">if relinKeys do not correspond to the top level parameters in the
362         /// current context</exception>
363         /// <exception cref="ArgumentException">if the size of relinKeys is too small</exception>
364         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
365         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
366         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RelinearizeInplace(Ciphertext encrypted, RelinKeys relinKeys, MemoryPoolHandle pool = null)367         public void RelinearizeInplace(Ciphertext encrypted, RelinKeys relinKeys,
368             MemoryPoolHandle pool = null)
369         {
370             Relinearize(encrypted, relinKeys, destination: encrypted, pool: pool);
371         }
372 
373         /// <summary>
374         /// Relinearizes a ciphertext.
375         /// </summary>
376         /// <remarks>
377         /// This functions relinearizes encrypted, reducing its size down to 2, and stores the result in the destination
378         /// parameter. If the size of encrypted is K+1, the given relinearization keys need to have size at least K-1.
379         /// Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
380         /// MemoryPoolHandle.
381         /// </remarks>
382         /// <param name="encrypted">The ciphertext to relinearize</param>
383         /// <param name="relinKeys">The relinearization keys</param>
384         /// <param name="destination">The ciphertext to overwrite with the relinearized result</param>
385         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
386         /// <exception cref="ArgumentNullException">if encrypted, relinKeys, or destination is null</exception>
387         /// <exception cref="ArgumentException">if encrypted or relinKeys is not valid for the encryption
388         /// parameters</exception>
389         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
390         /// <exception cref="ArgumentException">if relinKeys do not correspond to the top level parameters in the
391         /// current context</exception>
392         /// <exception cref="ArgumentException">if the size of relinKeys is too small</exception>
393         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
394         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
395         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Relinearize(Ciphertext encrypted, RelinKeys relinKeys, Ciphertext destination, MemoryPoolHandle pool = null)396         public void Relinearize(Ciphertext encrypted, RelinKeys relinKeys,
397             Ciphertext destination, MemoryPoolHandle pool = null)
398         {
399             if (null == encrypted)
400                 throw new ArgumentNullException(nameof(encrypted));
401             if (null == relinKeys)
402                 throw new ArgumentNullException(nameof(relinKeys));
403             if (null == destination)
404                 throw new ArgumentNullException(nameof(destination));
405             if (!ContextUsingKeyswitching)
406                 throw new InvalidOperationException("Keyswitching is not supported by the context");
407 
408             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
409             NativeMethods.Evaluator_Relinearize(
410                 NativePtr, encrypted.NativePtr, relinKeys.NativePtr, destination.NativePtr, poolPtr);
411         }
412 
413         /// <summary>
414         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
415         /// stores the result in the destination parameter.
416         /// </summary>
417         /// <remarks>
418         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
419         /// stores the result in the destination parameter. Dynamic memory allocations in the process are allocated from
420         /// the memory pool pointed to by the given MemoryPoolHandle.
421         /// </remarks>
422         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
423         /// <param name="destination">The ciphertext to overwrite with the modulus switched result</param>
424         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
425         /// <exception cref="ArgumentNullException">if encrypted, or destination is null</exception>
426         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
427         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
428         /// <exception cref="ArgumentException">if encrypted is already at lowest level</exception>
429         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
430         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
431         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ModSwitchToNext(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)432         public void ModSwitchToNext(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)
433         {
434             if (null == encrypted)
435                 throw new ArgumentNullException(nameof(encrypted));
436             if (null == destination)
437                 throw new ArgumentNullException(nameof(destination));
438 
439             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
440             NativeMethods.Evaluator_ModSwitchToNext(
441                 NativePtr, encrypted.NativePtr, destination.NativePtr, poolPtr);
442         }
443 
444         /// <summary>
445         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}.
446         /// </summary>
447         /// <remarks>
448         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}.
449         /// Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
450         /// MemoryPoolHandle.
451         /// </remarks>
452         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
453         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
454         /// <exception cref="ArgumentNullException">if encrypted is null</exception>
455         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
456         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
457         /// <exception cref="ArgumentException">if encrypted is already at lowest level</exception>
458         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
459         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
460         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ModSwitchToNextInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)461         public void ModSwitchToNextInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)
462         {
463             ModSwitchToNext(encrypted, destination: encrypted, pool: pool);
464         }
465 
466         /// <summary>
467         /// Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1}.
468         /// </summary>
469         /// <param name="plain">The plaintext to be switched to a smaller modulus</param>
470         /// <exception cref="ArgumentNullException">if plain is null</exception>
471         /// <exception cref="ArgumentException">if plain is not in NTT form</exception>
472         /// <exception cref="ArgumentException">if plain is not valid for the encryption parameters</exception>
473         /// <exception cref="ArgumentException">if plain is already at lowest level</exception>
474         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
ModSwitchToNextInplace(Plaintext plain)475         public void ModSwitchToNextInplace(Plaintext plain)
476         {
477             ModSwitchToNext(plain, destination: plain);
478         }
479 
480         /// <summary>
481         /// Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1} and stores
482         /// the result in the destination parameter.
483         /// </summary>
484         /// <param name="plain">The plaintext to be switched to a smaller modulus</param>
485         /// <param name="destination">destination The plaintext to overwrite with the modulus switched result</param>
486         /// <exception cref="ArgumentNullException">if plain, or destination is null</exception>
487         /// <exception cref="ArgumentException">if plain is not in NTT form</exception>
488         /// <exception cref="ArgumentException">if plain is not valid for the encryption parameters</exception>
489         /// <exception cref="ArgumentException">if plain is already at lowest level</exception>
490         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
491         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
ModSwitchToNext(Plaintext plain, Plaintext destination)492         public void ModSwitchToNext(Plaintext plain, Plaintext destination)
493         {
494             if (null == plain)
495                 throw new ArgumentNullException(nameof(plain));
496             if (null == destination)
497                 throw new ArgumentNullException(nameof(destination));
498 
499             NativeMethods.Evaluator_ModSwitchToNext(NativePtr, plain.NativePtr, destination.NativePtr);
500         }
501 
502         /// <summary>
503         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
504         /// reach the given ParmsId.
505         /// </summary>
506         /// <remarks>
507         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
508         /// reach the given ParmsId. Dynamic memory allocations in the process are allocated from the memory pool
509         /// pointed to by the given MemoryPoolHandle.
510         /// </remarks>
511         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
512         /// <param name="parmsId">The target parmsId</param>
513         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
514         /// <exception cref="ArgumentNullException">if encrypted or parmsId is null</exception>
515         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
516         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
517         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
518         /// <exception cref="ArgumentException">if encrypted is already at lower level in modulus chain than the
519         /// parameters corresponding to parmsId</exception>
520         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
521         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
522         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ModSwitchToInplace(Ciphertext encrypted, ParmsId parmsId, MemoryPoolHandle pool = null)523         public void ModSwitchToInplace(Ciphertext encrypted, ParmsId parmsId, MemoryPoolHandle pool = null)
524         {
525             ModSwitchTo(encrypted, parmsId, destination: encrypted, pool: pool);
526         }
527 
528         /// <summary>
529         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
530         /// reach the given ParmsId and stores the result in the destination parameter.
531         /// </summary>
532         /// <remarks>
533         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
534         /// reach the given ParmsId and stores the result in the destination parameter. Dynamic memory allocations in
535         /// the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
536         /// </remarks>
537         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
538         /// <param name="parmsId">The target parmsId</param>
539         /// <param name="destination">The ciphertext to overwrite with the modulus switched result</param>
540         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
541         /// <exception cref="ArgumentNullException">if encrypted, parmsId, or destination is null</exception>
542         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
543         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
544         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
545         /// <exception cref="ArgumentException">if encrypted is already at lower level in modulus chain than the
546         /// parameters corresponding to parmsId</exception>
547         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
548         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
549         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ModSwitchTo(Ciphertext encrypted, ParmsId parmsId, Ciphertext destination, MemoryPoolHandle pool = null)550         public void ModSwitchTo(Ciphertext encrypted, ParmsId parmsId, Ciphertext destination, MemoryPoolHandle pool = null)
551         {
552             if (null == encrypted)
553                 throw new ArgumentNullException(nameof(encrypted));
554             if (null == parmsId)
555                 throw new ArgumentNullException(nameof(parmsId));
556             if (null == destination)
557                 throw new ArgumentNullException(nameof(destination));
558 
559             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
560             NativeMethods.Evaluator_ModSwitchTo(
561                 NativePtr, encrypted.NativePtr, parmsId.Block, destination.NativePtr, poolPtr);
562         }
563 
564         /// <summary>
565         /// Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the
566         /// parameters reach the given ParmsId.
567         /// </summary>
568         /// <param name="plain">The plaintext to be switched to a smaller modulus</param>
569         /// <param name="parmsId">The target parmsId</param>
570         /// <exception cref="ArgumentNullException">if plain or parmsId is null</exception>
571         /// <exception cref="ArgumentException">if plain is not in NTT form</exception>
572         /// <exception cref="ArgumentException">if plain is not valid for the encryption parameters</exception>
573         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
574         /// <exception cref="ArgumentException">if plain is already at lower level in modulus chain than the parameters
575         /// corresponding to parmsId</exception>
576         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
ModSwitchToInplace(Plaintext plain, ParmsId parmsId)577         public void ModSwitchToInplace(Plaintext plain, ParmsId parmsId)
578         {
579             ModSwitchTo(plain, parmsId, destination: plain);
580         }
581 
582         /// <summary>
583         /// Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the
584         /// parameters reach the given ParmsId and stores the result in the destination parameter.
585         /// </summary>
586         /// <param name="plain">The plaintext to be switched to a smaller modulus</param>
587         /// <param name="parmsId">The target parmsId</param>
588         /// <param name="destination">The plaintext to overwrite with the modulus switched result</param>
589         /// <exception cref="ArgumentNullException">if plain, parmsId, or destination is null</exception>
590         /// <exception cref="ArgumentException">if plain is not in NTT form</exception>
591         /// <exception cref="ArgumentException">if plain is not valid for the encryption parameters</exception>
592         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
593         /// <exception cref="ArgumentException">if plain is already at lower level in modulus chain than the parameters
594         /// corresponding to parmsId</exception>
595         /// <exception cref="ArgumentException">if the scale is too large for the new encryption parameters</exception>
ModSwitchTo(Plaintext plain, ParmsId parmsId, Plaintext destination)596         public void ModSwitchTo(Plaintext plain, ParmsId parmsId,
597             Plaintext destination)
598         {
599             if (null == plain)
600                 throw new ArgumentNullException(nameof(plain));
601             if (null == parmsId)
602                 throw new ArgumentNullException(nameof(parmsId));
603             if (null == destination)
604                 throw new ArgumentNullException(nameof(destination));
605 
606             NativeMethods.Evaluator_ModSwitchTo(NativePtr, plain.NativePtr, parmsId.Block, destination.NativePtr);
607         }
608 
609         /// <summary>
610         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1},
611         /// scales the message down accordingly, and stores the result in the destination parameter.
612         /// </summary>
613         /// <remarks>
614         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1},
615         /// scales the message down accordingly, and stores the result in the destination parameter. Dynamic memory
616         /// allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
617         /// </remarks>
618         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
619         /// <param name="destination">The ciphertext to overwrite with the modulus switched result</param>
620         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
621         /// <exception cref="ArgumentNullException">if encrypted, or destination is null</exception>
622         /// <exception cref="ArgumentException">if the scheme is invalid for rescaling</exception>
623         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
624         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
625         /// <exception cref="ArgumentException">if encrypted is already at lowest level</exception>
626         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
627         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RescaleToNext(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)628         public void RescaleToNext(Ciphertext encrypted, Ciphertext destination, MemoryPoolHandle pool = null)
629         {
630             if (null == encrypted)
631                 throw new ArgumentNullException(nameof(encrypted));
632             if (null == destination)
633                 throw new ArgumentNullException(nameof(destination));
634 
635             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
636             NativeMethods.Evaluator_RescaleToNext(
637                 NativePtr, encrypted.NativePtr, destination.NativePtr, poolPtr);
638         }
639 
640         /// <summary>
641         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
642         /// scales the message down accordingly.
643         /// </summary>
644         /// <remarks>
645         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and
646         /// scales the message down accordingly. Dynamic memory allocations in the process are allocated from the memory
647         /// pool pointed to by the given MemoryPoolHandle.
648         /// </remarks>
649         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
650         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
651         /// <exception cref="ArgumentNullException">if encrypted is null</exception>
652         /// <exception cref="ArgumentException">if the scheme is invalid for rescaling</exception>
653         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
654         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form guaranteed to be</exception>
655         /// <exception cref="ArgumentException">if encrypted is already at lowest level</exception>
656         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
657         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RescaleToNextInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)658         public void RescaleToNextInplace(Ciphertext encrypted, MemoryPoolHandle pool = null)
659         {
660             RescaleToNext(encrypted, destination: encrypted, pool: pool);
661         }
662 
663         /// <summary>
664         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
665         /// reach the given ParmsId and scales the message down accordingly.
666         /// </summary>
667         /// <remarks>
668         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
669         /// reach the given ParmsId and scales the message down accordingly. Dynamic memory allocations in the process
670         /// are allocated from the memory pool pointed to by the given MemoryPoolHandle.
671         /// </remarks>
672         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
673         /// <param name="parmsId">The target parmsId</param>
674         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
675         /// <exception cref="ArgumentNullException">if encrypted or parmsId is null</exception>
676         /// <exception cref="ArgumentException">if the scheme is invalid for rescaling</exception>
677         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
678         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
679         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
680         /// <exception cref="ArgumentException">if encrypted is already at lower level in modulus chain than the
681         /// parameters corresponding to parmsId</exception>
682         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
683         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RescaleToInplace(Ciphertext encrypted, ParmsId parmsId, MemoryPoolHandle pool = null)684         public void RescaleToInplace(Ciphertext encrypted, ParmsId parmsId, MemoryPoolHandle pool = null)
685         {
686             RescaleTo(encrypted, parmsId, destination: encrypted, pool: pool);
687         }
688 
689         /// <summary>
690         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
691         /// reach the given ParmsId, scales the message down accordingly, and stores the result in the destination
692         /// parameter.
693         /// </summary>
694         /// <remarks>
695         /// Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters
696         /// reach the given ParmsId, scales the message down accordingly, and stores the result in the destination
697         /// parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the
698         /// given MemoryPoolHandle.
699         /// </remarks>
700         /// <param name="encrypted">The ciphertext to be switched to a smaller modulus</param>
701         /// <param name="parmsId">The target parmsId</param>
702         /// <param name="destination">The ciphertext to overwrite with the modulus switched result</param>
703         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
704         /// <exception cref="ArgumentNullException">if encrypted, parmsId, or destination is null.</exception>
705         /// <exception cref="ArgumentException">if the scheme is invalid for rescaling</exception>
706         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
707         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
708         /// <exception cref="ArgumentException">if parmsId is not valid for the encryption parameters</exception>
709         /// <exception cref="ArgumentException">if encrypted is already at lower level in modulus chain than the
710         /// parameters corresponding to parmsId</exception>
711         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
712         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RescaleTo(Ciphertext encrypted, ParmsId parmsId, Ciphertext destination, MemoryPoolHandle pool = null)713         public void RescaleTo(Ciphertext encrypted, ParmsId parmsId, Ciphertext destination, MemoryPoolHandle pool = null)
714         {
715             if (null == encrypted)
716                 throw new ArgumentNullException(nameof(encrypted));
717             if (null == parmsId)
718                 throw new ArgumentNullException(nameof(parmsId));
719             if (null == destination)
720                 throw new ArgumentNullException(nameof(destination));
721 
722             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
723             NativeMethods.Evaluator_RescaleTo(
724                 NativePtr, encrypted.NativePtr, parmsId.Block, destination.NativePtr, poolPtr);
725         }
726 
727         /// <summary>
728         /// Multiplies several ciphertexts together. This function computes the product of several ciphertext given as
729         /// an IEnumerable and stores the result in the destination parameter.
730         /// </summary>
731         /// <remarks>
732         /// Multiplies several ciphertexts together. This function computes the product of several ciphertext given as
733         /// an IEnumerable and stores the result in the destination parameter. The multiplication is done in a
734         /// depth-optimal order, and relinearization is performed automatically after every multiplication in the
735         /// process. In relinearization the given relinearization keys are used. Dynamic memory allocations in the
736         /// process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
737         /// </remarks>
738         /// <param name="encrypteds">The ciphertexts to multiply</param>
739         /// <param name="relinKeys">The relinearization keys</param>
740         /// <param name="destination">The ciphertext to overwrite with the multiplication result</param>
741         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
742         /// <exception cref="ArgumentNullException">if encrypteds, relinKeys, or destination is null</exception>
743         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
744         /// <exception cref="ArgumentException">if encrypteds is empty</exception>
745         /// <exception cref="ArgumentException">if encrypteds or relinKeys are not valid for the encryption
746         /// parameters</exception>
747         /// <exception cref="ArgumentException">if encrypteds are not in the default NTT form</exception>
748         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
749         /// parameters</exception>
750         /// <exception cref="ArgumentException">if the size of relinKeys is too small</exception>
751         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
752         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
753         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
MultiplyMany(IEnumerable<Ciphertext> encrypteds, RelinKeys relinKeys, Ciphertext destination, MemoryPoolHandle pool = null)754         public void MultiplyMany(IEnumerable<Ciphertext> encrypteds, RelinKeys relinKeys,
755             Ciphertext destination, MemoryPoolHandle pool = null)
756         {
757             if (null == encrypteds)
758                 throw new ArgumentNullException(nameof(encrypteds));
759             if (null == relinKeys)
760                 throw new ArgumentNullException(nameof(relinKeys));
761             if (null == destination)
762                 throw new ArgumentNullException(nameof(destination));
763             if (!ContextUsingKeyswitching)
764                 throw new InvalidOperationException("Keyswitching is not supported by the context");
765 
766             IntPtr[] encarray = encrypteds.Select(c => c.NativePtr).ToArray();
767             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
768             NativeMethods.Evaluator_MultiplyMany(
769                 NativePtr, (ulong)encarray.Length, encarray, relinKeys.NativePtr,
770                 destination.NativePtr, poolPtr);
771         }
772 
773         /// <summary>
774         /// Exponentiates a ciphertext.
775         /// </summary>
776         /// <remarks>
777         /// This functions raises encrypted to a power. Dynamic memory allocations in the process are allocated from the
778         /// memory pool pointed to by the given MemoryPoolHandle. The exponentiation is done in a depth-optimal order,
779         /// and relinearization is performed automatically after every multiplication in the process. In relinearization
780         /// the given relinearization keys are used.
781         /// </remarks>
782         /// <param name="encrypted">The ciphertext to exponentiate</param>
783         /// <param name="exponent">The power to raise the ciphertext to</param>
784         /// <param name="relinKeys">The relinearization keys</param>
785         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
786         /// <exception cref="ArgumentNullException">if encrypted or relinKeys is null</exception>
787         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
788         /// <exception cref="ArgumentException">if encrypted or relinKeys is not valid for the encryption
789         /// parameters</exception>
790         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
791         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
792         /// parameters</exception>
793         /// <exception cref="ArgumentException">if exponent is zero</exception>
794         /// <exception cref="ArgumentException">if the size of relinKeys is too small</exception>
795         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
796         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
797         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ExponentiateInplace(Ciphertext encrypted, ulong exponent, RelinKeys relinKeys, MemoryPoolHandle pool = null)798         public void ExponentiateInplace(Ciphertext encrypted, ulong exponent,
799             RelinKeys relinKeys, MemoryPoolHandle pool = null)
800         {
801             Exponentiate(encrypted, exponent, relinKeys, destination: encrypted, pool: pool);
802         }
803 
804         /// <summary>
805         /// Exponentiates a ciphertext.
806         /// </summary>
807         /// <remarks>
808         /// This functions raises encrypted to a power and stores the result in the destination parameter. Dynamic
809         /// memory allocations in the process are allocated from the memory pool pointed to by the given
810         /// MemoryPoolHandle. The exponentiation is done in a depth-optimal order, and relinearization is performed
811         /// automatically after every multiplication in the process. In relinearization the given relinearization keys
812         /// are used.
813         /// </remarks>
814         /// <param name="encrypted">The ciphertext to exponentiate</param>
815         /// <param name="exponent">The power to raise the ciphertext to</param>
816         /// <param name="relinKeys">The relinearization keys</param>
817         /// <param name="destination">The ciphertext to overwrite with the power</param>
818         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
819         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
820         /// <exception cref="ArgumentException">if encrypted or relinKeys is not valid for the encryption
821         /// parameters</exception>
822         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
823         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
824         /// parameters</exception>
825         /// <exception cref="ArgumentException">if exponent is zero</exception>
826         /// <exception cref="ArgumentException">if the size of relinKeys is too small</exception>
827         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
828         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
829         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
Exponentiate(Ciphertext encrypted, ulong exponent, RelinKeys relinKeys, Ciphertext destination, MemoryPoolHandle pool = null)830         public void Exponentiate(Ciphertext encrypted, ulong exponent, RelinKeys relinKeys,
831             Ciphertext destination, MemoryPoolHandle pool = null)
832         {
833             if (null == encrypted)
834                 throw new ArgumentNullException(nameof(encrypted));
835             if (null == relinKeys)
836                 throw new ArgumentNullException(nameof(relinKeys));
837             if (null == destination)
838                 throw new ArgumentNullException(nameof(destination));
839             if (!ContextUsingKeyswitching)
840                 throw new InvalidOperationException("Keyswitching is not supported by the context");
841 
842             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
843             NativeMethods.Evaluator_Exponentiate(
844                 NativePtr, encrypted.NativePtr, exponent, relinKeys.NativePtr,
845                 destination.NativePtr, poolPtr);
846         }
847 
848         /// <summary>
849         /// Adds a ciphertext and a plaintext and stores the result in encrypted.
850         /// </summary>
851         /// <param name="encrypted">The ciphertext to add</param>
852         /// <param name="plain">The plaintext to add</param>
853         /// <exception cref="ArgumentNullException">if encrypted or plain is null</exception>
854         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
855         /// parameters</exception>
856         /// <exception cref="ArgumentException">if encrypted or plain is not in the default NTT form</exception>
857         /// <exception cref="ArgumentException">if encrypted and plain are at different level or scale</exception>
858         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
AddPlainInplace(Ciphertext encrypted, Plaintext plain)859         public void AddPlainInplace(Ciphertext encrypted, Plaintext plain)
860         {
861             AddPlain(encrypted, plain, destination: encrypted);
862         }
863 
864         /// <summary>
865         /// Adds a ciphertext and a plaintext.
866         /// </summary>
867         /// <remarks>
868         /// This function adds a ciphertext and a plaintext and stores the result in the destination parameter.
869         /// </remarks>
870         /// <param name="encrypted">The ciphertext to add</param>
871         /// <param name="plain">The plaintext to add</param>
872         /// <param name="destination">The ciphertext to overwrite with the addition result</param>
873         /// <exception cref="ArgumentNullException">if encrypted, plain, or destination is null</exception>
874         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
875         /// parameters</exception>
876         /// <exception cref="ArgumentException">if encrypted or plain is not in the default NTT form</exception>
877         /// <exception cref="ArgumentException">if encrypted and plain are at different level or scale</exception>
878         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
AddPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination)879         public void AddPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination)
880         {
881             if (null == encrypted)
882                 throw new ArgumentNullException(nameof(encrypted));
883             if (null == plain)
884                 throw new ArgumentNullException(nameof(plain));
885             if (null == destination)
886                 throw new ArgumentNullException(nameof(destination));
887 
888             NativeMethods.Evaluator_AddPlain(
889                 NativePtr, encrypted.NativePtr, plain.NativePtr, destination.NativePtr);
890         }
891 
892         /// <summary>
893         /// Subtracts a plaintext from a ciphertext and stores the result in encrypted.
894         /// </summary>
895         /// <param name="encrypted">The ciphertext to subtract from</param>
896         /// <param name="plain">The plaintext to subtract</param>
897         /// <exception cref="ArgumentNullException">if encrypted or plain is null</exception>
898         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
899         /// parameters</exception>
900         /// <exception cref="ArgumentException">if encrypted or plain is not in the default NTT form</exception>
901         /// <exception cref="ArgumentException">if encrypted and plain are at different level or scale</exception>
902         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
SubPlainInplace(Ciphertext encrypted, Plaintext plain)903         public void SubPlainInplace(Ciphertext encrypted, Plaintext plain)
904         {
905             SubPlain(encrypted, plain, destination: encrypted);
906         }
907 
908         /// <summary>
909         /// Subtracts a plaintext from a ciphertext.
910         /// </summary>
911         /// <remarks>
912         /// This function subtracts a plaintext from a ciphertext and stores the result in the destination parameter.
913         /// </remarks>
914         /// <param name="encrypted">The ciphertext to subtract from</param>
915         /// <param name="plain">The plaintext to subtract</param>
916         /// <param name="destination">The ciphertext to overwrite with the subtraction result</param>
917         /// <exception cref="ArgumentNullException">if encrypted, plain, or destination is null</exception>
918         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
919         /// parameters</exception>
920         /// <exception cref="ArgumentException">if encrypted or plain is not in the default NTT form</exception>
921         /// <exception cref="ArgumentException">if encrypted and plain are at different level or scale</exception>
922         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
SubPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination)923         public void SubPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination)
924         {
925             if (null == encrypted)
926                 throw new ArgumentNullException(nameof(encrypted));
927             if (null == plain)
928                 throw new ArgumentNullException(nameof(plain));
929             if (null == destination)
930                 throw new ArgumentNullException(nameof(destination));
931 
932             NativeMethods.Evaluator_SubPlain(
933                 NativePtr, encrypted.NativePtr, plain.NativePtr, destination.NativePtr);
934         }
935 
936         /// <summary>
937         /// Multiplies a ciphertext with a plaintext and stores the result in encrypted.
938         /// </summary>
939         /// <remarks>
940         /// Multiplies a ciphertext with a plaintext. The plaintext cannot be identically 0. Dynamic memory allocations
941         /// in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
942         /// </remarks>
943         /// <param name="encrypted">The ciphertext to multiply</param>
944         /// <param name="plain">The plaintext to multiply</param>
945         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
946         /// <exception cref="ArgumentNullException">if encrypted or plain is null.</exception>
947         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
948         /// parameters</exception>
949         /// <exception cref="ArgumentException">if encrypted and plain are in different NTT forms</exception>
950         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
951         /// parameters</exception>
952         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
953         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
MultiplyPlainInplace(Ciphertext encrypted, Plaintext plain, MemoryPoolHandle pool = null)954         public void MultiplyPlainInplace(Ciphertext encrypted, Plaintext plain, MemoryPoolHandle pool = null)
955         {
956             MultiplyPlain(encrypted, plain, destination: encrypted, pool: pool);
957         }
958 
959         /// <summary>
960         /// Multiplies a ciphertext with a plaintext.
961         /// </summary>
962         /// <remarks>
963         /// This function multiplies a ciphertext with a plaintext and stores the result in the destination parameter.
964         /// The plaintext cannot be identically 0. Dynamic memory allocations in the process are allocated from the
965         /// memory pool pointed to by the given MemoryPoolHandle.
966         /// </remarks>
967         /// <param name="encrypted">The ciphertext to multiply</param>
968         /// <param name="plain">The plaintext to multiply</param>
969         /// <param name="destination">The ciphertext to overwrite with the multiplication result</param>
970         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
971         /// <exception cref="ArgumentNullException">if encrypted, plain, or destination is null</exception>
972         /// <exception cref="ArgumentException">if encrypted or plain is not valid for the encryption
973         /// parameters</exception>
974         /// <exception cref="ArgumentException">if encrypted and plain are in different NTT forms</exception>
975         /// <exception cref="ArgumentException">if the output scale is too large for the encryption
976         /// parameters</exception>
977         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
978         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
MultiplyPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination, MemoryPoolHandle pool = null)979         public void MultiplyPlain(Ciphertext encrypted, Plaintext plain, Ciphertext destination,
980             MemoryPoolHandle pool = null)
981         {
982             if (null == encrypted)
983                 throw new ArgumentNullException(nameof(encrypted));
984             if (null == plain)
985                 throw new ArgumentNullException(nameof(plain));
986             if (null == destination)
987                 throw new ArgumentNullException(nameof(destination));
988 
989             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
990             NativeMethods.Evaluator_MultiplyPlain(
991                 NativePtr, encrypted.NativePtr, plain.NativePtr, destination.NativePtr, poolPtr);
992         }
993 
994         /// <summary>
995         /// Transforms a plaintext to NTT domain.
996         /// </summary>
997         /// <remarks>
998         /// This functions applies the Number Theoretic Transform to a plaintext by first embedding integers modulo the
999         /// plaintext modulus to integers modulo the coefficient modulus and then performing David Harvey's NTT on the
1000         /// resulting polynomial. The transformation is done with respect to encryption parameters corresponding to a
1001         /// given parmsId. For the operation to be valid, the plaintext must have degree less than PolyModulusDegree and
1002         /// each coefficient must be less than the plaintext modulus, i.e., the plaintext must be a valid plaintext
1003         /// under the current encryption parameters. Dynamic memory allocations in the process are allocated from the
1004         /// memory pool pointed to by the given MemoryPoolHandle.
1005         /// </remarks>
1006         /// <param name="plain">The plaintext to transform</param>
1007         /// <param name="parmsId">The ParmsId with respect to which the NTT is done</param>
1008         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1009         /// <exception cref="ArgumentNullException">if plain or parmsId is null</exception>
1010         /// <exception cref="ArgumentException">if plain is already in NTT form</exception>
1011         /// <exception cref="ArgumentException">if plain or parmsId is not valid for the encryption
1012         /// parameters</exception>
1013         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
TransformToNTTInplace(Plaintext plain, ParmsId parmsId, MemoryPoolHandle pool = null)1014         public void TransformToNTTInplace(Plaintext plain, ParmsId parmsId, MemoryPoolHandle pool = null)
1015         {
1016             TransformToNTT(plain, parmsId, destinationNTT: plain, pool: pool);
1017         }
1018 
1019         /// <summary>
1020         /// Transforms a plaintext to NTT domain.
1021         /// </summary>
1022         /// <remarks>
1023         /// This functions applies the Number Theoretic Transform to a plaintext by first embedding integers modulo the
1024         /// plaintext modulus to integers modulo the coefficient modulus and then performing David Harvey's NTT on the
1025         /// resulting polynomial. The transformation is done with respect to encryption parameters corresponding to a
1026         /// given ParmsId. The result is stored in the destinationNTT parameter. For the operation to be valid, the
1027         /// plaintext must have degree less than PolyModulusDegree and each coefficient must be less than the plaintext
1028         /// modulus, i.e., the plaintext must be a valid plaintext under the current encryption parameters. Dynamic
1029         /// memory allocations in the process are allocated from the memory pool pointed to by the given
1030         /// MemoryPoolHandle.
1031         /// </remarks>
1032         /// <param name="plain">The plaintext to transform</param>
1033         /// <param name="parmsId">The ParmsId with respect to which the NTT is done</param>
1034         /// <param name="destinationNTT">The plaintext to overwrite with the transformed result</param>
1035         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1036         /// <exception cref="ArgumentNullException">if plain, parmsId, or destinationNTT is null</exception>
1037         /// <exception cref="ArgumentException">if plain is already in NTT form</exception>
1038         /// <exception cref="ArgumentException">if plain or parmsId is not valid for the encryption
1039         /// parameters</exception>
1040         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
TransformToNTT(Plaintext plain, ParmsId parmsId, Plaintext destinationNTT, MemoryPoolHandle pool = null)1041         public void TransformToNTT(Plaintext plain, ParmsId parmsId,
1042             Plaintext destinationNTT, MemoryPoolHandle pool = null)
1043         {
1044             if (null == plain)
1045                 throw new ArgumentNullException(nameof(plain));
1046             if (null == parmsId)
1047                 throw new ArgumentNullException(nameof(parmsId));
1048             if (null == destinationNTT)
1049                 throw new ArgumentNullException(nameof(destinationNTT));
1050 
1051             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1052             NativeMethods.Evaluator_TransformToNTT(NativePtr, plain.NativePtr, parmsId.Block, destinationNTT.NativePtr, poolPtr);
1053         }
1054 
1055         /// <summary>
1056         /// Transforms a ciphertext to NTT domain.
1057         /// </summary>
1058         /// <remarks>
1059         /// Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform
1060         /// separately to each polynomial of a ciphertext.
1061         /// </remarks>
1062         /// <param name="encrypted">The ciphertext to transform</param>
1063         /// <exception cref="ArgumentNullException">if encrypted is null</exception>
1064         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
1065         /// <exception cref="ArgumentException">if encrypted is already in NTT form</exception>
1066         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
TransformToNTTInplace(Ciphertext encrypted)1067         public void TransformToNTTInplace(Ciphertext encrypted)
1068         {
1069             TransformToNTT(encrypted, destinationNTT: encrypted);
1070         }
1071 
1072         /// <summary>
1073         /// Transforms a ciphertext to NTT domain.
1074         /// </summary>
1075         /// <remarks>
1076         /// Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform
1077         /// separately to each polynomial of a ciphertext. The result is stored in the DestinationNTT parameter.
1078         /// </remarks>
1079         /// <param name="encrypted">The ciphertext to transform</param>
1080         /// <param name="destinationNTT">The ciphertext to overwrite with the transformed result</param>
1081         /// <exception cref="ArgumentNullException">if encrypted or destinationNTT is null</exception>
1082         /// <exception cref="ArgumentException">if encrypted is not valid for the encryption parameters</exception>
1083         /// <exception cref="ArgumentException">if encrypted is already in NTT form</exception>
1084         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
TransformToNTT(Ciphertext encrypted, Ciphertext destinationNTT)1085         public void TransformToNTT(Ciphertext encrypted, Ciphertext destinationNTT)
1086         {
1087             if (null == encrypted)
1088                 throw new ArgumentNullException(nameof(encrypted));
1089             if (null == destinationNTT)
1090                 throw new ArgumentNullException(nameof(destinationNTT));
1091 
1092             NativeMethods.Evaluator_TransformToNTT(
1093                 NativePtr, encrypted.NativePtr, destinationNTT.NativePtr);
1094         }
1095 
1096         /// <summary>
1097         /// Transforms a ciphertext back from NTT domain.
1098         /// </summary>
1099         /// <remarks>
1100         /// Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number
1101         /// Theoretic Transform separately to each polynomial of a ciphertext.
1102         /// </remarks>
1103         /// <param name="encryptedNTT">The ciphertext to transform</param>
1104         /// <exception cref="ArgumentNullException">if encryptedNTT is null</exception>
1105         /// <exception cref="ArgumentException">if encryptedNTT is not valid for the encryption parameters</exception>
1106         /// <exception cref="ArgumentException">if encryptedNTT is not in NTT form</exception>
1107         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
TransformFromNTTInplace(Ciphertext encryptedNTT)1108         public void TransformFromNTTInplace(Ciphertext encryptedNTT)
1109         {
1110             TransformFromNTT(encryptedNTT, destination: encryptedNTT);
1111         }
1112 
1113         /// <summary>
1114         /// Transforms a ciphertext back from NTT domain.
1115         /// </summary>
1116         /// <remarks>
1117         /// Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number
1118         /// Theoretic Transform separately to each polynomial of a ciphertext. The result is stored in the destination
1119         /// parameter.
1120         /// </remarks>
1121         /// <param name="encryptedNTT">The ciphertext to transform</param>
1122         /// <param name="destination">The ciphertext to overwrite with the transformed result</param>
1123         /// <exception cref="ArgumentNullException">if encryptedNTT or destination is null</exception>
1124         /// <exception cref="ArgumentException">if encryptedNTT is not valid for the encryption parameters</exception>
1125         /// <exception cref="ArgumentException">if encryptedNTT is not in NTT form</exception>
1126         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
TransformFromNTT(Ciphertext encryptedNTT, Ciphertext destination)1127         public void TransformFromNTT(Ciphertext encryptedNTT, Ciphertext destination)
1128         {
1129             if (null == encryptedNTT)
1130                 throw new ArgumentNullException(nameof(encryptedNTT));
1131             if (null == destination)
1132                 throw new ArgumentNullException(nameof(destination));
1133 
1134             NativeMethods.Evaluator_TransformFromNTT(
1135                 NativePtr, encryptedNTT.NativePtr, destination.NativePtr);
1136         }
1137 
1138         /// <summary>
1139         /// Applies a Galois automorphism to a ciphertext.
1140         /// </summary>
1141         /// <remarks>
1142         /// <para>
1143         /// Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, an appropriate set of
1144         /// Galois keys must also be provided. Dynamic memory allocations in the process are allocated from the memory
1145         /// pool pointed to by the given MemoryPoolHandle.
1146         /// </para>
1147         /// <para>
1148         /// The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval
1149         /// [1, M-1], where M = 2*N, and N = PolyModulusDegree. Used with batching, a Galois element 3^i % M corresponds
1150         /// to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row
1151         /// rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV,
1152         /// and complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois
1153         /// element p changes Enc(plain(x)) to Enc(plain(x^p)).
1154         /// </para>
1155         /// </remarks>
1156         /// <param name="encrypted">The ciphertext to apply the Galois automorphism to</param>
1157         /// <param name="galoisElt">The Galois element</param>
1158         /// <param name="galoisKeys">The Galois keys</param>
1159         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1160         /// <exception cref="ArgumentNullException">if encrypted or galoisKeys is null</exception>
1161         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1162         /// parameters</exception>
1163         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1164         /// current context</exception>
1165         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
1166         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1167         /// <exception cref="ArgumentException">if the Galois element is not valid</exception>
1168         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1169         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1170         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1171         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ApplyGaloisInplace(Ciphertext encrypted, uint galoisElt, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)1172         public void ApplyGaloisInplace(Ciphertext encrypted, uint galoisElt,
1173             GaloisKeys galoisKeys, MemoryPoolHandle pool = null)
1174         {
1175             ApplyGalois(encrypted, galoisElt, galoisKeys, destination: encrypted, pool: pool);
1176         }
1177 
1178         /// <summary>
1179         /// Applies a Galois automorphism to a ciphertext and writes the result to the destination parameter.
1180         /// </summary>
1181         /// <remarks>
1182         /// <para>
1183         /// Applies a Galois automorphism to a ciphertext and writes the result to the destination parameter. To
1184         /// evaluate the Galois automorphism, an appropriate set of Galois keys must also be provided. Dynamic memory
1185         /// allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
1186         /// </para>
1187         /// <para>
1188         /// The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval
1189         /// [1, M-1], where M = 2*N, and N = PolyModulusDegree. Used with batching, a Galois element 3^i % M corresponds
1190         /// to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row
1191         /// rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV,
1192         /// and complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois
1193         /// element p changes Enc(plain(x)) to Enc(plain(x^p)).
1194         /// </para>
1195         /// </remarks>
1196         /// <param name="encrypted">The ciphertext to apply the Galois automorphism to</param>
1197         /// <param name="galoisElt">The Galois element</param>
1198         /// <param name="galoisKeys">The Galois keys</param>
1199         /// <param name="destination">The ciphertext to overwrite with the result</param>
1200         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1201         /// <exception cref="ArgumentNullException">if encrypted, galoisKeys, or destination is null</exception>
1202         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1203         /// parameters</exception>
1204         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1205         /// current context</exception>
1206         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
1207         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1208         /// <exception cref="ArgumentException">if the Galois element is not valid</exception>
1209         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1210         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1211         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1212         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ApplyGalois(Ciphertext encrypted, uint galoisElt, GaloisKeys galoisKeys, Ciphertext destination, MemoryPoolHandle pool = null)1213         public void ApplyGalois(Ciphertext encrypted, uint galoisElt, GaloisKeys galoisKeys,
1214             Ciphertext destination, MemoryPoolHandle pool = null)
1215         {
1216             if (null == encrypted)
1217                 throw new ArgumentNullException(nameof(encrypted));
1218             if (null == galoisKeys)
1219                 throw new ArgumentNullException(nameof(galoisKeys));
1220             if (null == destination)
1221                 throw new ArgumentNullException(nameof(destination));
1222             if (!ContextUsingKeyswitching)
1223                 throw new InvalidOperationException("Keyswitching is not supported by the context");
1224 
1225             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1226             NativeMethods.Evaluator_ApplyGalois(
1227                 NativePtr, encrypted.NativePtr, galoisElt,
1228                 galoisKeys.NativePtr, destination.NativePtr, poolPtr);
1229         }
1230 
1231         /// <summary>
1232         /// Rotates plaintext matrix rows cyclically.
1233         /// </summary>
1234         /// <remarks>
1235         /// When batching is used with the BFV scheme, this function rotates the encrypted plaintext matrix rows
1236         /// cyclically to the left (steps &gt; 0) or to the right (steps &lt; 0). Since the size of the batched matrix
1237         /// is 2-by-(N/2), where N is the degree of the polynomial modulus, the number of steps to rotate must have
1238         /// absolute value at most N/2-1. Dynamic memory allocations in the process are allocated from the memory pool
1239         /// pointed to by the given MemoryPoolHandle.
1240         /// </remarks>
1241         /// <param name="encrypted">The ciphertext to rotate</param>
1242         /// <param name="steps">The number of steps to rotate (positive left, negative right)</param>
1243         /// <param name="galoisKeys">The Galois keys</param>
1244         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1245         /// <exception cref="ArgumentNullException">if encrypted or galoisKeys is null</exception>
1246         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1247         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
1248         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1249         /// parameters</exception>
1250         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1251         /// current context</exception>
1252         /// <exception cref="ArgumentException">if encrypted is not in the default NTT form</exception>
1253         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1254         /// <exception cref="ArgumentException">if steps has too big absolute value</exception>
1255         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1256         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1257         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1258         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateRowsInplace(Ciphertext encrypted, int steps, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)1259         public void RotateRowsInplace(Ciphertext encrypted,
1260             int steps, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)
1261         {
1262             RotateRows(encrypted, steps, galoisKeys, destination: encrypted, pool: pool);
1263         }
1264 
1265         /// <summary>
1266         /// Rotates plaintext matrix rows cyclically.
1267         /// </summary>
1268         /// <remarks>
1269         /// When batching is used with the BFV scheme, this function rotates the encrypted plaintext matrix rows
1270         /// cyclically to the left (steps &gt; 0) or to the right (steps &lt; 0) and writes the result to the
1271         /// destination parameter. Since the size of the batched matrix is 2-by-(N/2), where N is the degree of the
1272         /// polynomial modulus, the number of steps to rotate must have absolute value at most N/2-1. Dynamic memory
1273         /// allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle.
1274         /// </remarks>
1275         /// <param name="encrypted">The ciphertext to rotate</param>
1276         /// <param name="steps">The number of steps to rotate (positive left, negative right)</param>
1277         /// <param name="galoisKeys">The Galois keys</param>
1278         /// <param name="destination">The ciphertext to overwrite with the rotated result</param>
1279         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1280         /// <exception cref="ArgumentNullException">if encrypted, galoisKeys, or destination is null</exception>
1281         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1282         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
1283         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1284         /// parameters</exception>
1285         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1286         /// current context</exception>
1287         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1288         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1289         /// <exception cref="ArgumentException">if steps has too big absolute value</exception>
1290         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1291         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1292         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1293         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateRows(Ciphertext encrypted, int steps, GaloisKeys galoisKeys, Ciphertext destination, MemoryPoolHandle pool = null)1294         public void RotateRows(Ciphertext encrypted, int steps, GaloisKeys galoisKeys,
1295             Ciphertext destination, MemoryPoolHandle pool = null)
1296         {
1297             if (null == encrypted)
1298                 throw new ArgumentNullException(nameof(encrypted));
1299             if (null == galoisKeys)
1300                 throw new ArgumentNullException(nameof(galoisKeys));
1301             if (null == destination)
1302                 throw new ArgumentNullException(nameof(destination));
1303             if (!ContextUsingKeyswitching)
1304                 throw new InvalidOperationException("Keyswitching is not supported by the context");
1305 
1306             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1307             NativeMethods.Evaluator_RotateRows(
1308                 NativePtr, encrypted.NativePtr, steps, galoisKeys.NativePtr,
1309                 destination.NativePtr, poolPtr);
1310         }
1311 
1312         /// <summary>
1313         /// Rotates plaintext matrix columns cyclically.
1314         /// </summary>
1315         /// <remarks>
1316         /// When batching is used with the BFV scheme, this function rotates the encrypted plaintext matrix columns
1317         /// cyclically. Since the size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial
1318         /// modulus, this means simply swapping the two rows. Dynamic memory allocations in the process are allocated
1319         /// from the memory pool pointed to by the given MemoryPoolHandle.
1320         /// </remarks>
1321         /// <param name="encrypted">The ciphertext to rotate</param>
1322         /// <param name="galoisKeys">The Galois keys</param>
1323         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1324         /// <exception cref="ArgumentNullException">if encrypted or galoisKeys is null</exception>
1325         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1326         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
1327         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1328         /// parameters</exception>
1329         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1330         /// current context</exception>
1331         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1332         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1333         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1334         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1335         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1336         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateColumnsInplace(Ciphertext encrypted, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)1337         public void RotateColumnsInplace(Ciphertext encrypted, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)
1338         {
1339             RotateColumns(encrypted, galoisKeys, destination: encrypted, pool: pool);
1340         }
1341 
1342         /// <summary>
1343         /// Rotates plaintext matrix columns cyclically.
1344         /// </summary>
1345         /// <remarks>
1346         /// When batching is used with the BFV scheme, this function rotates the encrypted plaintext matrix columns
1347         /// cyclically, and writes the result to the destination parameter. Since the size of the batched matrix is
1348         /// 2-by-(N/2), where N is the degree of the polynomial modulus, this means simply swapping the two rows.
1349         /// Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
1350         /// MemoryPoolHandle.
1351         /// </remarks>
1352         /// <param name="encrypted">The ciphertext to rotate</param>
1353         /// <param name="galoisKeys">The Galois keys</param>
1354         /// <param name="destination">The ciphertext to overwrite with the rotated result</param>
1355         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1356         /// <exception cref="ArgumentNullException">if encrypted, galoisKeys, or destination is null</exception>
1357         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1358         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.BFV</exception>
1359         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1360         /// parameters</exception>
1361         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1362         /// current context</exception>
1363         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1364         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1365         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1366         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1367         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1368         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateColumns(Ciphertext encrypted, GaloisKeys galoisKeys, Ciphertext destination, MemoryPoolHandle pool = null)1369         public void RotateColumns(Ciphertext encrypted, GaloisKeys galoisKeys,
1370             Ciphertext destination, MemoryPoolHandle pool = null)
1371         {
1372             if (null == encrypted)
1373                 throw new ArgumentNullException(nameof(encrypted));
1374             if (null == galoisKeys)
1375                 throw new ArgumentNullException(nameof(galoisKeys));
1376             if (null == destination)
1377                 throw new ArgumentNullException(nameof(destination));
1378             if (!ContextUsingKeyswitching)
1379                 throw new InvalidOperationException("Keyswitching is not supported by the context");
1380 
1381             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1382             NativeMethods.Evaluator_RotateColumns(
1383                 NativePtr, encrypted.NativePtr, galoisKeys.NativePtr,
1384                 destination.NativePtr, poolPtr);
1385         }
1386 
1387         /// <summary>
1388         /// Rotates plaintext vector cyclically.
1389         /// </summary>
1390         /// <remarks>
1391         /// When using the CKKS scheme, this function rotates the encrypted plaintext vector cyclically to the left
1392         /// (steps &gt; 0) or to the right (steps &lt; 0). Since the size of the batched matrix is 2-by-(N/2), where N
1393         /// is the degree of the polynomial modulus, the number of steps to rotate must have absolute value at most
1394         /// N/2-1. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given
1395         /// MemoryPoolHandle.
1396         /// </remarks>
1397         /// <param name="encrypted">The ciphertext to rotate</param>
1398         /// <param name="steps">The number of steps to rotate (positive left, negative right)</param>
1399         /// <param name="galoisKeys">The Galois keys</param>
1400         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1401         /// <exception cref="ArgumentNullException">if encrypted or galoisKeys is null</exception>
1402         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1403         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.CKKS</exception>
1404         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1405         /// parameters</exception>
1406         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1407         /// current context</exception>
1408         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1409         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1410         /// <exception cref="ArgumentException">if steps has too big absolute value</exception>
1411         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1412         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1413         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1414         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateVectorInplace(Ciphertext encrypted, int steps, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)1415         public void RotateVectorInplace(Ciphertext encrypted, int steps,
1416             GaloisKeys galoisKeys, MemoryPoolHandle pool = null)
1417         {
1418             RotateVector(encrypted, steps, galoisKeys, destination: encrypted, pool: pool);
1419         }
1420 
1421         /// <summary>
1422         /// Rotates plaintext vector cyclically.
1423         /// </summary>
1424         /// <remarks>
1425         /// When using the CKKS scheme, this function rotates the encrypted plaintext vector cyclically to the left
1426         /// (steps &gt; 0) or to the right (steps &lt; 0) and writes the result to the destination parameter. Since the
1427         /// size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, the number of
1428         /// steps to rotate must have absolute value at most N/2-1. Dynamic memory allocations in the process are
1429         /// allocated from the memory pool pointed to by the given MemoryPoolHandle.
1430         /// </remarks>
1431         /// <param name="encrypted">The ciphertext to rotate</param>
1432         /// <param name="steps">The number of steps to rotate (positive left, negative right)</param>
1433         /// <param name="galoisKeys">The Galois keys</param>
1434         /// <param name="destination">The ciphertext to overwrite with the rotated result</param>
1435         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1436         /// <exception cref="ArgumentNullException">if encrypted, galoisKeys, or destination is null</exception>
1437         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1438         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.CKKS</exception>
1439         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1440         /// parameters</exception>
1441         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1442         /// current context</exception>
1443         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1444         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1445         /// <exception cref="ArgumentException">if steps has too big absolute value</exception>
1446         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1447         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1448         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1449         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
RotateVector(Ciphertext encrypted, int steps, GaloisKeys galoisKeys, Ciphertext destination, MemoryPoolHandle pool = null)1450         public void RotateVector(Ciphertext encrypted, int steps, GaloisKeys galoisKeys,
1451             Ciphertext destination, MemoryPoolHandle pool = null)
1452         {
1453             if (null == encrypted)
1454                 throw new ArgumentNullException(nameof(encrypted));
1455             if (null == galoisKeys)
1456                 throw new ArgumentNullException(nameof(galoisKeys));
1457             if (null == destination)
1458                 throw new ArgumentNullException(nameof(destination));
1459             if (!ContextUsingKeyswitching)
1460                 throw new InvalidOperationException("Keyswitching is not supported by the context");
1461 
1462             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1463             NativeMethods.Evaluator_RotateVector(
1464                 NativePtr, encrypted.NativePtr, steps, galoisKeys.NativePtr,
1465                 destination.NativePtr, poolPtr);
1466         }
1467 
1468         /// <summary>
1469         /// Complex conjugates plaintext slot values.
1470         /// </summary>
1471         /// <remarks>
1472         /// When using the CKKS scheme, this function complex conjugates all values in the underlying plaintext. Dynamic
1473         /// memory allocations in the process are allocated from the memory pool pointed to by the given
1474         /// MemoryPoolHandle.
1475         /// </remarks>
1476         /// <param name="encrypted">The ciphertext to rotate</param>
1477         /// <param name="galoisKeys">The Galois keys</param>
1478         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1479         /// <exception cref="ArgumentNullException">if encrypted or galoisKeys is null</exception>
1480         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1481         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.CKKS</exception>
1482         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1483         /// parameters</exception>
1484         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1485         /// current context</exception>
1486         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1487         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1488         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1489         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1490         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1491         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ComplexConjugateInplace(Ciphertext encrypted, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)1492         public void ComplexConjugateInplace(Ciphertext encrypted, GaloisKeys galoisKeys, MemoryPoolHandle pool = null)
1493         {
1494             ComplexConjugate(encrypted, galoisKeys, destination: encrypted, pool: pool);
1495         }
1496 
1497         /// <summary>
1498         /// Complex conjugates plaintext slot values.
1499         /// </summary>
1500         /// <remarks>
1501         /// When using the CKKS scheme, this function complex conjugates all values in the underlying plaintext, and
1502         /// writes the result to the destination parameter. Dynamic memory allocations in the process are allocated from
1503         /// the memory pool pointed to by the given MemoryPoolHandle.
1504         /// </remarks>
1505         /// <param name="encrypted">The ciphertext to rotate</param>
1506         /// <param name="galoisKeys">The Galois keys</param>
1507         /// <param name="destination">The ciphertext to overwrite with the rotated result</param>
1508         /// <param name="pool">The MemoryPoolHandle pointing to a valid memory pool</param>
1509         /// <exception cref="InvalidOperationException">if the encryption parameters do not support batching</exception>
1510         /// <exception cref="InvalidOperationException">if scheme is not SchemeType.CKKS</exception>
1511         /// <exception cref="ArgumentException">if encrypted or galoisKeys is not valid for the encryption
1512         /// parameters</exception>
1513         /// <exception cref="ArgumentException">if galoisKeys do not correspond to the top level parameters in the
1514         /// current context</exception>
1515         /// <exception cref="ArgumentException">if encrypted is in NTT form</exception>
1516         /// <exception cref="ArgumentException">if encrypted has size larger than 2</exception>
1517         /// <exception cref="ArgumentException">if necessary Galois keys are not present</exception>
1518         /// <exception cref="ArgumentException">if pool is uninitialized</exception>
1519         /// <exception cref="InvalidOperationException">if keyswitching is not supported by the context</exception>
1520         /// <exception cref="InvalidOperationException">if result ciphertext is transparent</exception>
ComplexConjugate(Ciphertext encrypted, GaloisKeys galoisKeys, Ciphertext destination, MemoryPoolHandle pool = null)1521         public void ComplexConjugate(Ciphertext encrypted, GaloisKeys galoisKeys,
1522             Ciphertext destination, MemoryPoolHandle pool = null)
1523         {
1524             if (null == encrypted)
1525                 throw new ArgumentNullException(nameof(encrypted));
1526             if (null == galoisKeys)
1527                 throw new ArgumentNullException(nameof(galoisKeys));
1528             if (null == destination)
1529                 throw new ArgumentNullException(nameof(destination));
1530             if (!ContextUsingKeyswitching)
1531                 throw new InvalidOperationException("Keyswitching is not supported by the context");
1532 
1533             IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
1534             NativeMethods.Evaluator_ComplexConjugate(
1535                 NativePtr, encrypted.NativePtr, galoisKeys.NativePtr,
1536                 destination.NativePtr, poolPtr);
1537         }
1538 
1539         /// <summary>
1540         /// Destroy native object.
1541         /// </summary>
DestroyNativeObject()1542         protected override void DestroyNativeObject()
1543         {
1544             NativeMethods.Evaluator_Destroy(NativePtr);
1545         }
1546 
1547         internal bool ContextUsingKeyswitching
1548         {
1549             get
1550             {
1551                 NativeMethods.Evaluator_ContextUsingKeyswitching(NativePtr, out bool result);
1552                 return result;
1553             }
1554         }
1555     }
1556 }
1557