// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Research.SEAL.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Research.SEAL
{
///
/// Provides functionality for CRT batching. If the polynomial modulus degree is N, and
/// the plaintext modulus is a prime number T such that T is congruent to 1 modulo 2N,
/// then BatchEncoder allows the plaintext elements to be viewed as 2-by-(N/2)
/// matrices of integers modulo T. Homomorphic operations performed on such encrypted
/// matrices are applied coefficient (slot) wise, enabling powerful SIMD functionality
/// for computations that are vectorizable. This functionality is often called "batching"
/// in the homomorphic encryption literature.
///
///
///
/// Mathematical Background
/// Mathematically speaking, if the polynomial modulus is X^N+1, N is a power of two, and
/// PlainModulus is a prime number T such that 2N divides T-1, then integers modulo T
/// contain a primitive 2N-th root of unity and the polynomial X^N+1 splits into n distinct
/// linear factors as X^N+1 = (X-a_1)*...*(X-a_N) mod T, where the constants a_1, ..., a_n
/// are all the distinct primitive 2N-th roots of unity in integers modulo T. The Chinese
/// Remainder Theorem (CRT) states that the plaintext space Z_T[X]/(X^N+1) in this case is
/// isomorphic (as an algebra) to the N-fold direct product of fields Z_T. The isomorphism
/// is easy to compute explicitly in both directions, which is what this class does.
/// Furthermore, the Galois group of the extension is (Z/2NZ)* ~= Z/2Z x Z/(N/2) whose
/// action on the primitive roots of unity is easy to describe. Since the batching slots
/// correspond 1-to-1 to the primitive roots of unity, applying Galois automorphisms on the
/// plaintext act by permuting the slots. By applying generators of the two cyclic
/// subgroups of the Galois group, we can effectively view the plaintext as a 2-by-(N/2)
/// matrix, and enable cyclic row rotations, and column rotations (row swaps).
///
///
/// Valid Parameters
/// Whether batching can be used depends on whether the plaintext modulus has been chosen
/// appropriately. Thus, to construct a BatchEncoder the user must provide an instance
/// of SEALContext such that its associated EncryptionParameterQualifiers object has the
/// flags ParametersSet and EnableBatching set to true.
///
///
/// see EncryptionParameters for more information about encryption parameters.
/// see EncryptionParameterQualifiers for more information about parameter qualifiers.
/// see Evaluator for rotating rows and columns of encrypted matrices.
public class BatchEncoder : NativeObject
{
///
/// Creates a BatchEncoder. It is necessary that the encryption parameters
/// given through the SEALContext object support batching.
///
/// The SEALContext
/// @param[in] context
/// if context is null.
/// if the context is not set or encryption
/// parameters are not valid for batching
/// if scheme is not SchemeType.BFV
public BatchEncoder(SEALContext context)
{
if (null == context)
throw new ArgumentNullException(nameof(context));
if (!context.ParametersSet)
throw new ArgumentException("Encryption parameters are not set correctly");
SEALContext.ContextData contextData = context.FirstContextData;
if (contextData.Parms.Scheme != SchemeType.BFV)
throw new ArgumentException("Unsupported scheme");
if (!contextData.Qualifiers.UsingBatching)
throw new ArgumentException("Encryption parameters are not valid for batching");
NativeMethods.BatchEncoder_Create(context.NativePtr, out IntPtr ptr);
NativePtr = ptr;
}
///
/// Creates a plaintext from a given matrix. This function "batches" a given matrix
/// of integers modulo the plaintext modulus into a plaintext element, and stores
/// the result in the destination parameter. The input vector must have size at most equal
/// to the degree of the polynomial modulus. The first half of the elements represent the
/// first row of the matrix, and the second half represent the second row. The numbers
/// in the matrix can be at most equal to the plaintext modulus for it to represent
/// a valid plaintext.
///
/// If the destination plaintext overlaps the input values in memory, the behavior of
/// this function is undefined.
///
/// The matrix of integers modulo plaintext modulus to batch
/// The plaintext polynomial to overwrite with the result
/// if either values or destination are null
/// if values is too large
public void Encode(IEnumerable values, Plaintext destination)
{
if (null == values)
throw new ArgumentNullException(nameof(values));
if (null == destination)
throw new ArgumentNullException(nameof(destination));
ulong[] valarray = values.ToArray();
NativeMethods.BatchEncoder_Encode(NativePtr, (ulong)valarray.LongLength, valarray, destination.NativePtr);
}
///
/// Creates a plaintext from a given matrix. This function "batches" a given matrix
/// of integers modulo the plaintext modulus into a plaintext element, and stores
/// the result in the destination parameter. The input vector must have size at most equal
/// to the degree of the polynomial modulus. The first half of the elements represent the
/// first row of the matrix, and the second half represent the second row. The numbers
/// in the matrix can be at most equal to the plaintext modulus for it to represent
/// a valid plaintext.
///
/// If the destination plaintext overlaps the input values in memory, the behavior of
/// this function is undefined.
///
/// The matrix of integers modulo plaintext modulus to batch
/// The plaintext polynomial to overwrite with the result
/// if either values or destionation are null
/// if values is too large
public void Encode(IEnumerable values, Plaintext destination)
{
if (null == values)
throw new ArgumentNullException(nameof(values));
if (null == destination)
throw new ArgumentNullException(nameof(destination));
long[] valarray = values.ToArray();
NativeMethods.BatchEncoder_Encode(NativePtr, (ulong)valarray.LongLength, valarray, destination.NativePtr);
}
///
/// Creates a plaintext from a given matrix. This function "batches" a given matrix
/// of integers modulo the plaintext modulus in-place into a plaintext ready to be
/// encrypted. The matrix is given as a plaintext element whose first N/2 coefficients
/// represent the first row of the matrix, and the second N/2 coefficients represent the
/// second row, where N denotes the degree of the polynomial modulus. The input plaintext
/// must have degress less than the polynomial modulus, and coefficients less than the
/// plaintext modulus, i.e. it must be a valid plaintext for the encryption parameters.
/// Dynamic memory allocations in the process are allocated from the memory pool pointed
/// to by the given MemoryPoolHandle.
///
/// The matrix of integers modulo plaintext modulus to batch
///
/// if plain is null.
/// if plain is not valid for the encryption parameters
/// if plain is in NTT form
/// if pool is uninitialized
public void Encode(Plaintext plain, MemoryPoolHandle pool = null)
{
if (null == plain)
throw new ArgumentNullException(nameof(plain));
IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
NativeMethods.BatchEncoder_Encode(NativePtr, plain.NativePtr, poolPtr);
}
///
/// Inverse of encode. This function "unbatches" a given plaintext into a matrix
/// of integers modulo the plaintext modulus, and stores the result in the destination
/// parameter. The input plaintext must have degress less than the polynomial modulus,
/// and coefficients less than the plaintext modulus, i.e. it must be a valid plaintext
/// for the encryption parameters. Dynamic memory allocations in the process are
/// allocated from the memory pool pointed to by the given MemoryPoolHandle.
///
/// The plaintext polynomial to unbatch
/// The matrix to be overwritten with the values in the slots
/// The MemoryPoolHandle pointing to a valid memory pool
/// if either plain or destionation are null
/// if plain is not valid for the encryption parameters
/// if plain is in NTT form
/// if pool is uninitialized
public void Decode(Plaintext plain, ICollection destination, MemoryPoolHandle pool = null)
{
if (null == plain)
throw new ArgumentNullException(nameof(plain));
if (null == destination)
throw new ArgumentNullException(nameof(destination));
IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
ulong destCount = 0;
// Allocate a big enough array to hold the result
ulong[] destArray = new ulong[SlotCount];
NativeMethods.BatchEncoder_Decode(NativePtr, plain.NativePtr, ref destCount, destArray, poolPtr);
// Transfer result to actual destination
destination.Clear();
for (ulong i = 0; i < destCount; i++)
{
destination.Add(destArray[i]);
}
}
///
/// Inverse of encode. This function "unbatches" a given plaintext into a matrix
/// of integers modulo the plaintext modulus, and stores the result in the destination
/// parameter. The input plaintext must have degress less than the polynomial modulus,
/// and coefficients less than the plaintext modulus, i.e. it must be a valid plaintext
/// for the encryption parameters. Dynamic memory allocations in the process are
/// allocated from the memory pool pointed to by the given MemoryPoolHandle.
///
/// The plaintext polynomial to unbatch
/// The matrix to be overwritten with the values in the slots
/// The MemoryPoolHandle pointing to a valid memory pool
/// if either plain or destination are null
/// if plain is not valid for the encryption parameters
/// if plain is in NTT form
/// if pool is uninitialized
public void Decode(Plaintext plain, ICollection destination, MemoryPoolHandle pool = null)
{
if (null == plain)
throw new ArgumentNullException(nameof(plain));
if (null == destination)
throw new ArgumentNullException(nameof(destination));
IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
ulong destCount = 0;
// Allocate a big enough array to hold the result
long[] destArray = new long[SlotCount];
NativeMethods.BatchEncoder_Decode(NativePtr, plain.NativePtr, ref destCount, destArray, poolPtr);
// Transfer result to actual destination
destination.Clear();
for (ulong i = 0; i < destCount; i++)
{
destination.Add(destArray[i]);
}
}
///
/// Inverse of encode. This function "unbatches" a given plaintext in-place into
/// a matrix of integers modulo the plaintext modulus. The input plaintext must have
/// degress less than the polynomial modulus, and coefficients less than the plaintext
/// modulus, i.e. it must be a valid plaintext for the encryption parameters. Dynamic
/// memory allocations in the process are allocated from the memory pool pointed to by
/// the given MemoryPoolHandle.
///
/// The plaintext polynomial to unbatch
/// The MemoryPoolHandle pointing to a valid memory pool
/// if plain is null
/// if plain is not valid for the encryption parameters
/// if plain is in NTT form
/// if pool is uninitialized
public void Decode(Plaintext plain, MemoryPoolHandle pool = null)
{
if (null == plain)
throw new ArgumentNullException(nameof(plain));
IntPtr poolPtr = pool?.NativePtr ?? IntPtr.Zero;
NativeMethods.BatchEncoder_Decode(NativePtr, plain.NativePtr, poolPtr);
}
///
/// Returns the number of slots.
///
public ulong SlotCount
{
get
{
NativeMethods.BatchEncoder_GetSlotCount(NativePtr, out ulong slotCount);
return slotCount;
}
}
///
/// Destroy native object
///
protected override void DestroyNativeObject()
{
NativeMethods.BatchEncoder_Destroy(NativePtr);
}
}
}