// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Research.SEAL.Tools;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Research.SEAL
{
/// Represent an integer modulus of up to 61 bits.
///
///
/// Represent an integer modulus of up to 61 bits. An instance of the Modulus
/// class represents a non-negative integer modulus up to 61 bits. In particular,
/// the encryption parameter PlainModulus, and the primes in CoeffModulus, are
/// represented by instances of Modulus. The purpose of this class is to
/// perform and store the pre-computation required by Barrett reduction.
///
///
/// In general, reading from Modulus is thread-safe as long as no other thread
/// is concurrently mutating it.
///
///
/// See EncryptionParameters for a description
/// of the encryption parameters.
public class Modulus : NativeObject,
IEquatable, IEquatable,
IComparable, IComparable
{
/// Creates a Modulus instance.
///
/// Creates a Modulus instance. The value of the Modulus is set to 0.
///
public Modulus()
{
NativeMethods.Modulus_Create(value: 0, smallModulus: out IntPtr ptr);
NativePtr = ptr;
}
/// Creates a Modulus instance.
///
/// Creates a Modulus instance. The value of the Modulus is set to
/// the given value.
///
/// The integer modulus
/// if value is 1 or more than
/// 61 bits
public Modulus(ulong value)
{
NativeMethods.Modulus_Create(value, out IntPtr ptr);
NativePtr = ptr;
}
/// Creates a new Modulus by copying a given one.
/// The Modulus to copy from
/// if copy is null
public Modulus(Modulus copy)
{
if (null == copy)
throw new ArgumentNullException(nameof(copy));
NativeMethods.Modulus_Create(copy.NativePtr, out IntPtr ptr);
NativePtr = ptr;
}
///
/// Creates a Modulus from a native pointer
///
/// Pointer to the native Modulus
/// Whether this instance owns the native pointer
internal Modulus(IntPtr sm, bool owned = true)
: base(sm, owned)
{
}
/// Copies a given Modulus to the current one.
/// The Modulus to copy from
/// if assign is null
public void Set(Modulus assign)
{
if (null == assign)
throw new ArgumentNullException(nameof(assign));
NativeMethods.Modulus_Set(NativePtr, assign.NativePtr);
}
/// Sets the value of the Modulus.
/// The new integer modulus
/// if value is 1 or more than
/// 61 bits
public void Set(ulong value)
{
NativeMethods.Modulus_Set(NativePtr, value);
}
///
/// Returns the significant bit count of the value of the current Modulus.
///
public int BitCount
{
get
{
NativeMethods.Modulus_BitCount(NativePtr, out int result);
return result;
}
}
///
/// Returns the size (in 64-bit words) of the value of the current Modulus.
///
public ulong UInt64Count
{
get
{
NativeMethods.Modulus_UInt64Count(NativePtr, out ulong result);
return result;
}
}
///
/// Returns the value of the current Modulus.
///
public ulong Value
{
get
{
NativeMethods.Modulus_Value(NativePtr, out ulong result);
return result;
}
}
///
/// Returns the Barrett ratio computed for the value of the current Modulus.
///
///
/// Returns the Barrett ratio computed for the value of the current Modulus.
/// The first two components of the Barrett ratio are the floor of 2^128/value,
/// and the third component is the remainder.
///
public Tuple ConstRatio
{
get
{
ulong[] ratio = new ulong[3];
NativeMethods.Modulus_ConstRatio(NativePtr, length: (ulong)3, ratio: ratio);
return new Tuple(ratio[0], ratio[1], ratio[2]);
}
}
///
/// Returns whether the value of the current Modulus is zero.
///
public bool IsZero
{
get
{
NativeMethods.Modulus_IsZero(NativePtr, out bool result);
return result;
}
}
///
/// Returns whether the value of the current Modulus is a prime number.
///
public bool IsPrime
{
get
{
NativeMethods.Modulus_IsPrime(NativePtr, out bool result);
return result;
}
}
///
/// Returns an upper bound on the size of the Modulus, as if it was
/// written to an output stream.
///
/// The compression mode
/// if the compression mode is not
/// supported
/// if the size does not fit in
/// the return type
public long SaveSize(ComprModeType? comprMode = null)
{
comprMode = comprMode ?? Serialization.ComprModeDefault;
if (!Serialization.IsSupportedComprMode(comprMode.Value))
throw new ArgumentException("Unsupported compression mode");
ComprModeType comprModeValue = comprMode.Value;
NativeMethods.Modulus_SaveSize(
NativePtr, (byte)comprModeValue, out long outBytes);
return outBytes;
}
/// Saves the Modulus to an output stream.
///
/// Saves the Modulus to an output stream. The output is in binary format
/// and not human-readable.
///
/// The stream to save the Modulus to
/// The desired compression mode
/// if stream is null
/// if the stream is closed or does not
/// support writing, or if compression mode is not supported
/// if I/O operations failed
/// if the data to be saved
/// is invalid, or if compression failed
public long Save(Stream stream, ComprModeType? comprMode = null)
{
comprMode = comprMode ?? Serialization.ComprModeDefault;
if (!Serialization.IsSupportedComprMode(comprMode.Value))
throw new ArgumentException("Unsupported compression mode");
ComprModeType comprModeValue = comprMode.Value;
return Serialization.Save(
(byte[] outptr, ulong size, byte cm, out long outBytes) =>
NativeMethods.Modulus_Save(NativePtr, outptr, size,
cm, out outBytes),
SaveSize(comprModeValue), comprModeValue, stream);
}
///
/// Loads a Modulus from an input stream overwriting the current Modulus.
///
/// The stream to load the Modulus from
/// if stream is null
/// if the stream is closed or does not
/// support reading
/// if the stream ended
/// unexpectedly
/// if I/O operations failed
/// if the data cannot be loaded
/// by this version of Microsoft SEAL, if the loaded data is invalid, or if the
/// loaded compression mode is not supported
public long Load(Stream stream)
{
return Serialization.Load(
(byte[] outptr, ulong size, out long outBytes) =>
NativeMethods.Modulus_Load(NativePtr, outptr, size,
out outBytes),
stream);
}
///
/// Reduces a given unsigned integer modulo this modulus.
///
/// The unsigned integer to reduce
///
public ulong Reduce(ulong value)
{
NativeMethods.Modulus_Reduce(NativePtr, value, out ulong result);
return result;
}
///
/// Returns a hash-code based on the value of the Modulus.
///
public override int GetHashCode()
{
ulong[] arr = new ulong[1];
arr[0] = Value;
return Utilities.ComputeArrayHashCode(arr);
}
///
/// Compares two Modulus instances.
///
/// The value to compare against
public override bool Equals(object obj)
{
Modulus sm = obj as Modulus;
return Equals(sm);
}
/// Creates a Modulus instance.
///
/// Creates a Modulus instance. The value of the Modulus is set to
/// the given value.
///
/// The integer modulus
/// if value is 1 or more than 61 bits
public static explicit operator Modulus(ulong value)
{
Modulus sm = new Modulus(value);
return sm;
}
#region IEquatable methods
///
/// Determines whether this instance equals another Modulus instance
///
/// Instance to compare against
public bool Equals(Modulus other)
{
if (null == other)
return false;
NativeMethods.Modulus_Equals(NativePtr, other.NativePtr, out bool result);
return result;
}
#endregion
#region IEquatable methods
///
/// Determines whether the value of this instance equals the given UInt64 value
///
/// The value to compare against
public bool Equals(ulong other)
{
NativeMethods.Modulus_Equals(NativePtr, other, out bool result);
return result;
}
#endregion
#region IComparable methods
///
/// Compares two Modulus instances.
///
/// The Modulus to compare against
public int CompareTo(Modulus compare)
{
if (null == compare)
return 1;
return Value.CompareTo(compare.Value);
}
#endregion
#region IComparable methods
///
/// Compares a Modulus value to an unsigned integer.
///
/// The unsigned integer to compare against
public int CompareTo(ulong compare)
{
return Value.CompareTo(compare);
}
#endregion
///
/// Destroy native object.
///
protected override void DestroyNativeObject()
{
NativeMethods.Modulus_Destroy(NativePtr);
}
}
///
/// Represents a standard security level according to the HomomorphicEncryption.org
/// security standard.
///
///
/// Represents a standard security level according to the HomomorphicEncryption.org
/// security standard. The value SecLevelType.None signals that no standard
/// security level should be imposed. The value SecLevelType.TC128 provides
/// a very high level of security and is the default security level enforced by
/// Microsoft SEAL when constructing a SEALContext object. Normal users should not
/// have to specify the security level explicitly anywhere.
///
public enum SecLevelType : int
{
///
/// No security level specified.
///
None = 0,
///
/// 128-bit security level according to HomomorphicEncryption.org standard.
///
TC128 = 128,
///
/// 192-bit security level according to HomomorphicEncryption.org standard.
///
TC192 = 192,
///
/// 256-bit security level according to HomomorphicEncryption.org standard.
///
TC256 = 256
}
///
/// This class contains static methods for creating a coefficient modulus easily.
///
///
///
/// This class contains static methods for creating a coefficient modulus easily.
/// Note that while these functions take a SecLevelType argument, all security
/// guarantees are lost if the output is used with encryption parameters with
/// a mismatching value for the PolyModulusDegree.
///
///
/// The default value SecLevelType.TC128 provides a very high level of security
/// and is the default security level enforced by Microsoft SEAL when constructing
/// a SEALContext object. Normal users should not have to specify the security
/// level explicitly anywhere.
///
///
public static class CoeffModulus
{
///
/// Returns the largest bit-length of the coefficient modulus, i.e., bit-length
/// of the product of the primes in the coefficient modulus, that guarantees
/// a given security level when using a given PolyModulusDegree, according
/// to the HomomorphicEncryption.org security standard.
///
/// The value of the PolyModulusDegree
/// encryption parameter
/// The desired standard security level
static public int MaxBitCount(ulong polyModulusDegree, SecLevelType secLevel = SecLevelType.TC128)
{
NativeMethods.CoeffModulus_MaxBitCount(polyModulusDegree, (int)secLevel, out int result);
return result;
}
///
/// Returns a default coefficient modulus for the BFV scheme that guarantees
/// a given security level when using a given PolyModulusDegree, according
/// to the HomomorphicEncryption.org security standard.
///
///
///
/// Returns a default coefficient modulus for the BFV scheme that guarantees
/// a given security level when using a given PolyModulusDegree, according
/// to the HomomorphicEncryption.org security standard. Note that all security
/// guarantees are lost if the output is used with encryption parameters with
/// a mismatching value for the PolyModulusDegree.
///
///
/// The coefficient modulus returned by this function will not perform well
/// if used with the CKKS scheme.
///
///
/// The value of the PolyModulusDegree
/// encryption parameter
/// The desired standard security level
/// if polyModulusDegree is not
/// a power-of-two or is too large
/// if secLevel is SecLevelType.None
static public IEnumerable BFVDefault(
ulong polyModulusDegree, SecLevelType secLevel = SecLevelType.TC128)
{
List result = null;
ulong length = 0;
NativeMethods.CoeffModulus_BFVDefault(polyModulusDegree, (int)secLevel, ref length, null);
IntPtr[] coeffArray = new IntPtr[length];
NativeMethods.CoeffModulus_BFVDefault(polyModulusDegree, (int)secLevel, ref length, coeffArray);
result = new List(checked((int)length));
foreach (IntPtr sm in coeffArray)
{
result.Add(new Modulus(sm));
}
return result;
}
///
/// Returns a custom coefficient modulus suitable for use with the specified
/// PolyModulusDegree.
///
///
/// Returns a custom coefficient modulus suitable for use with the specified
/// PolyModulusDegree.The return value will be a vector consisting of
/// Modulus elements representing distinct prime numbers of bit-lengths
/// as given in the bitSizes parameter. The bit sizes of the prime numbers
/// can be at most 60 bits.
///
/// The value of the PolyModulusDegree encryption parameter
/// The bit-lengths of the primes to be generated
/// if polyModulusDegree is not
/// a power-of-two or is too large
/// if bitSizes is too large or if its
/// elements are out of bounds
/// if not enough suitable primes could be found
static public IEnumerable Create(
ulong polyModulusDegree, IEnumerable bitSizes)
{
if (null == bitSizes)
throw new ArgumentNullException(nameof(bitSizes));
List result = null;
int[] bitSizesArr = bitSizes.ToArray();
int length = bitSizesArr.Length;
IntPtr[] coeffArray = new IntPtr[length];
NativeMethods.CoeffModulus_Create(polyModulusDegree, (ulong)length, bitSizesArr, coeffArray);
result = new List(length);
foreach (IntPtr sm in coeffArray)
{
result.Add(new Modulus(sm));
}
return result;
}
///
/// Returns a custom coefficient modulus suitable for use with the specified
/// PolyModulusDegree.
///
///
/// Returns a custom coefficient modulus suitable for use with the specified
/// PolyModulusDegree.The return value will be a vector consisting of
/// Modulus elements representing distinct prime numbers of bit-lengths
/// as given in the bitSizes parameter. The bit sizes of the prime numbers
/// can be at most 60 bits.
///
/// The value of the PolyModulusDegree encryption parameter
/// The value of the PlainModulus encryption parameter
/// The bit-lengths of the primes to be generated
/// if polyModulusDegree is not
/// a power-of-two or is too large
/// if bitSizes is too large or if its
/// elements are out of bounds
/// if LCM(2*polyModulusDegree, PlainModulus) is
/// more than 64-bit
/// if not enough suitable primes could be found
static public IEnumerable Create(
ulong polyModulusDegree, Modulus plainModulus, IEnumerable bitSizes)
{
if (null == bitSizes)
throw new ArgumentNullException(nameof(bitSizes));
List result = null;
int[] bitSizesArr = bitSizes.ToArray();
int length = bitSizesArr.Length;
IntPtr[] coeffArray = new IntPtr[length];
NativeMethods.CoeffModulus_Create(polyModulusDegree, (ulong)length, bitSizesArr, plainModulus.NativePtr,
coeffArray);
result = new List(length);
foreach (IntPtr sm in coeffArray)
{
result.Add(new Modulus(sm));
}
return result;
}
}
///
/// This class contains static methods for creating a plaintext modulus easily.
///
public static class PlainModulus
{
///
/// Creates a prime number Modulus for use as PlainModulus encryption
/// parameter that supports batching with a given PolyModulusDegree.
///
/// The value of the PolyModulusDegree
/// encryption parameter
/// The bit-length of the prime to be generated
/// if polyModulusDegree is not
/// a power-of-two or is too large
/// if bitSize is out of bounds
/// if a suitable prime could not be found
static public Modulus Batching(ulong polyModulusDegree, int bitSize)
{
return CoeffModulus.Create(
polyModulusDegree,
new int[] { bitSize }).First();
}
///
/// Creates several prime number Modulus elements that can be used as
/// PlainModulus encryption parameters, each supporting batching with a given
/// PolyModulusDegree.
///
/// The value of the PolyModulusDegree
/// encryption parameter
/// The bit-lengths of the primes to be generated
/// if polyModulusDegree is not
/// a power-of-two or is too large
/// if bitSizes is too large or if its
/// elements are out of bounds
/// if not enough suitable primes could be found
static public IEnumerable Batching(
ulong polyModulusDegree, IEnumerable bitSizes)
{
return CoeffModulus.Create(polyModulusDegree, bitSizes);
}
}
}