public void SEALContextCreateTest() { EncryptionParameters encParams1 = new EncryptionParameters(SchemeType.BFV); EncryptionParameters encParams2 = new EncryptionParameters(SchemeType.CKKS); SEALContext context1 = new SEALContext(encParams1); SEALContext context2 = new SEALContext(encParams2); Assert.IsNotNull(context1); Assert.IsNotNull(context2); Assert.IsFalse(context1.ParametersSet); Assert.IsFalse(context2.ParametersSet); Assert.AreNotSame(context1.FirstParmsId, context1.LastParmsId); Assert.AreEqual(context1.FirstParmsId, context1.LastParmsId); SEALContext.ContextData data1 = context2.FirstContextData; SEALContext.ContextData data2 = context2.GetContextData(context2.FirstParmsId); Assert.AreNotSame(data1, data2); ulong[] totalCoeffMod1 = data1.TotalCoeffModulus; ulong[] totalCoeffMod2 = data2.TotalCoeffModulus; int bitCount1 = data1.TotalCoeffModulusBitCount; int bitCount2 = data2.TotalCoeffModulusBitCount; Assert.AreEqual(bitCount1, bitCount2); Assert.AreEqual(totalCoeffMod1.Length, totalCoeffMod2.Length); for (int i = 0; i < totalCoeffMod1.Length; i++) { Assert.AreEqual(totalCoeffMod1[i], totalCoeffMod2[i]); } }
public void ExpandModChainTest() { EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 4096, CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree: 4096), PlainModulus = new Modulus(1 << 20) }; SEALContext context1 = new SEALContext(parms, expandModChain: true, secLevel: SecLevelType.None); // By default there is a chain SEALContext.ContextData contextData = context1.KeyContextData; Assert.IsNotNull(contextData); Assert.IsNull(contextData.PrevContextData); Assert.IsNotNull(contextData.NextContextData); contextData = context1.FirstContextData; Assert.IsNotNull(contextData); Assert.IsNotNull(contextData.PrevContextData); Assert.IsNotNull(contextData.NextContextData); // This should not create a chain SEALContext context2 = new SEALContext(parms, expandModChain: false); contextData = context2.KeyContextData; Assert.IsNotNull(contextData); Assert.IsNull(contextData.PrevContextData); Assert.IsNotNull(contextData.NextContextData); contextData = context2.FirstContextData; Assert.IsNotNull(contextData); Assert.IsNotNull(contextData.PrevContextData); Assert.IsNull(contextData.NextContextData); }
/// <summary> /// Helper function: Prints the parameters in a SEALContext. /// </summary> public static void PrintParameters(SEALContext context) { // Verify parameters if (null == context) { throw new ArgumentNullException("context is not set"); } SEALContext.ContextData contextData = context.KeyContextData; /* * Which scheme are we using? */ string schemeName = null; switch (contextData.Parms.Scheme) { case SchemeType.BFV: schemeName = "BFV"; break; case SchemeType.CKKS: schemeName = "CKKS"; break; default: throw new ArgumentException("unsupported scheme"); } Console.WriteLine("/"); Console.WriteLine("| Encryption parameters:"); Console.WriteLine($"| Scheme: {schemeName}"); Console.WriteLine("| PolyModulusDegree: {0}", contextData.Parms.PolyModulusDegree); /* * Print the size of the true (product) coefficient modulus. */ Console.Write("| CoeffModulus size: {0} (", contextData.TotalCoeffModulusBitCount); List <SmallModulus> coeffModulus = (List <SmallModulus>)contextData.Parms.CoeffModulus; for (int i = 0; i < coeffModulus.Count - 1; i++) { Console.Write($"{coeffModulus[i].BitCount} + "); } Console.WriteLine($"{coeffModulus.Last().BitCount}) bits"); /* * For the BFV scheme print the PlainModulus parameter. */ if (contextData.Parms.Scheme == SchemeType.BFV) { Console.WriteLine("| PlainModulus: {0}", contextData.Parms.PlainModulus.Value); } Console.WriteLine("\\"); }
public void SEALContextParamsTest() { List <SmallModulus> coeffModulus = new List <SmallModulus> { DefaultParams.SmallMods30Bit(0), DefaultParams.SmallMods30Bit(1), DefaultParams.SmallMods30Bit(2) }; EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 128, PlainModulus = new SmallModulus(1 << 6), CoeffModulus = coeffModulus }; SEALContext context = SEALContext.Create(parms); SEALContext.ContextData data = context.FirstContextData; Assert.IsNotNull(data); EncryptionParameters parms2 = data.Parms; Assert.AreEqual(parms.PolyModulusDegree, parms2.PolyModulusDegree); Assert.AreEqual(parms.NoiseStandardDeviation, parms2.NoiseStandardDeviation); EncryptionParameterQualifiers qualifiers = data.Qualifiers; Assert.IsNotNull(qualifiers); Assert.IsTrue(qualifiers.ParametersSet); Assert.IsFalse(qualifiers.UsingBatching); Assert.IsTrue(qualifiers.UsingFastPlainLift); Assert.IsTrue(qualifiers.UsingFFT); Assert.IsTrue(qualifiers.UsingNTT); Assert.IsTrue(qualifiers.UsingHEStdSecurity); ulong[] cdpm = data.CoeffDivPlainModulus; Assert.AreEqual(3, cdpm.Length); Assert.AreEqual(32ul, data.PlainUpperHalfThreshold); Assert.AreEqual(3, data.PlainUpperHalfIncrement.Length); Assert.IsNull(data.UpperHalfThreshold); Assert.IsNotNull(data.UpperHalfIncrement); Assert.AreEqual(3, data.UpperHalfIncrement.Length); Assert.AreEqual(2ul, data.ChainIndex); SEALContext.ContextData data2 = data.NextContextData; Assert.IsNotNull(data2); Assert.AreEqual(1ul, data2.ChainIndex); SEALContext.ContextData data3 = data2.NextContextData; Assert.IsNotNull(data3); Assert.AreEqual(0ul, data3.ChainIndex); Assert.IsNull(data3.NextContextData); }
public void SEALContextParamsTest() { EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 128, PlainModulus = new SmallModulus(1 << 6), CoeffModulus = CoeffModulus.Create(128, new int[] { 30, 30, 30 }) }; SEALContext context = new SEALContext(parms, expandModChain: true, secLevel: SecLevelType.None); SEALContext.ContextData data = context.KeyContextData; Assert.IsNotNull(data); EncryptionParameters parms2 = data.Parms; Assert.AreEqual(parms.PolyModulusDegree, parms2.PolyModulusDegree); EncryptionParameterQualifiers qualifiers = data.Qualifiers; Assert.IsNotNull(qualifiers); Assert.IsTrue(qualifiers.ParametersSet); Assert.IsFalse(qualifiers.UsingBatching); Assert.IsTrue(qualifiers.UsingFastPlainLift); Assert.IsTrue(qualifiers.UsingFFT); Assert.IsTrue(qualifiers.UsingNTT); Assert.AreEqual(SecLevelType.None, qualifiers.SecLevel); Assert.IsFalse(qualifiers.UsingDescendingModulusChain); Assert.IsTrue(context.UsingKeyswitching); ulong[] cdpm = data.CoeffDivPlainModulus; Assert.AreEqual(3, cdpm.Length); Assert.AreEqual(32ul, data.PlainUpperHalfThreshold); Assert.AreEqual(3, data.PlainUpperHalfIncrement.Length); Assert.IsNull(data.UpperHalfThreshold); Assert.IsNotNull(data.UpperHalfIncrement); Assert.AreEqual(3, data.UpperHalfIncrement.Length); Assert.AreEqual(2ul, data.ChainIndex); Assert.IsNull(data.PrevContextData); SEALContext.ContextData data2 = data.NextContextData; Assert.IsNotNull(data2); Assert.AreEqual(1ul, data2.ChainIndex); Assert.AreEqual(2ul, data2.PrevContextData.ChainIndex); SEALContext.ContextData data3 = data2.NextContextData; Assert.IsNotNull(data3); Assert.AreEqual(0ul, data3.ChainIndex); Assert.AreEqual(1ul, data3.PrevContextData.ChainIndex); Assert.IsNull(data3.NextContextData); }
/// <summary> /// Helper function: Prints the parameters in a SEALContext. /// </summary> public static void PrintParameters(SEALContext context) { // Verify parameters if (null == context) { throw new ArgumentNullException("context is not set"); } SEALContext.ContextData contextData = context.FirstContextData; /* * Which scheme are we using? */ string schemeName = null; switch (contextData.Parms.Scheme) { case SchemeType.BFV: schemeName = "BFV"; break; case SchemeType.CKKS: schemeName = "CKKS"; break; default: throw new ArgumentException("unsupported scheme"); } Console.WriteLine($"/ Encryption parameters:"); Console.WriteLine($"| Scheme: {schemeName}"); Console.WriteLine($"| PolyModulusDegree: {contextData.Parms.PolyModulusDegree}"); /* * Print the size of the true (product) coefficient modulus. */ Console.WriteLine($"| CoeffModulus size: {contextData.TotalCoeffModulusBitCount} bits"); /* * For the BFV scheme print the plain_modulus parameter. */ if (contextData.Parms.Scheme == SchemeType.BFV) { Console.WriteLine($"| PlainModulus: {contextData.Parms.PlainModulus.Value}"); } Console.WriteLine($"\\ NoiseStandardDeviation: {contextData.Parms.NoiseStandardDeviation}"); Console.WriteLine(); }
public void SEALContextCKKSParamsTest() { int slotSize = 4; List <SmallModulus> coeffModulus = new List <SmallModulus> { DefaultParams.SmallMods40Bit(0), DefaultParams.SmallMods40Bit(1), DefaultParams.SmallMods40Bit(2), DefaultParams.SmallMods40Bit(3) }; EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS) { PolyModulusDegree = 2 * (ulong)slotSize, CoeffModulus = coeffModulus }; SEALContext context = SEALContext.Create(parms); SEALContext.ContextData data = context.FirstContextData; Assert.IsNotNull(data); // This should be available in CKKS Assert.IsNotNull(data.UpperHalfThreshold); Assert.AreEqual(4, data.UpperHalfThreshold.Length); Assert.IsNull(data.UpperHalfIncrement); Assert.AreEqual(3ul, data.ChainIndex); SEALContext.ContextData data2 = data.NextContextData; Assert.IsNotNull(data2); Assert.AreEqual(2ul, data2.ChainIndex); SEALContext.ContextData data3 = data2.NextContextData; Assert.IsNotNull(data3); Assert.AreEqual(1ul, data3.ChainIndex); SEALContext.ContextData data4 = data3.NextContextData; Assert.IsNotNull(data4); Assert.AreEqual(0ul, data4.ChainIndex); Assert.IsNull(data4.NextContextData); }
public void SEALContextCKKSParamsTest() { int slotSize = 4; EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS) { PolyModulusDegree = 2 * (ulong)slotSize, CoeffModulus = CoeffModulus.Create(2 * (ulong)slotSize, new int[] { 40, 40, 40, 40 }) }; SEALContext context = new SEALContext(parms, expandModChain: true, secLevel: SecLevelType.None); SEALContext.ContextData data = context.KeyContextData; Assert.IsNotNull(data); // This should be available in CKKS Assert.IsNotNull(data.UpperHalfThreshold); Assert.AreEqual(4, data.UpperHalfThreshold.Length); Assert.IsNull(data.UpperHalfIncrement); Assert.AreEqual(3ul, data.ChainIndex); Assert.IsNull(data.PrevContextData); SEALContext.ContextData data2 = data.NextContextData; Assert.IsNotNull(data2); Assert.AreEqual(2ul, data2.ChainIndex); Assert.AreEqual(3ul, data2.PrevContextData.ChainIndex); SEALContext.ContextData data3 = data2.NextContextData; Assert.IsNotNull(data3); Assert.AreEqual(1ul, data3.ChainIndex); Assert.AreEqual(2ul, data3.PrevContextData.ChainIndex); SEALContext.ContextData data4 = data3.NextContextData; Assert.IsNotNull(data4); Assert.AreEqual(0ul, data4.ChainIndex); Assert.AreEqual(1ul, data4.PrevContextData.ChainIndex); Assert.IsNull(data4.NextContextData); }
public void ExpandModChainTest() { EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 4096, CoeffModulus = DefaultParams.CoeffModulus128(polyModulusDegree: 4096), PlainModulus = new SmallModulus(1 << 20) }; SEALContext context1 = SEALContext.Create(parms); // By default there is a chain SEALContext.ContextData contextData = context1.FirstContextData; Assert.IsNotNull(contextData); Assert.IsNotNull(contextData.NextContextData); // This should not create a chain SEALContext context2 = SEALContext.Create(parms, expandModChain: false); contextData = context2.FirstContextData; Assert.IsNotNull(contextData); Assert.IsNull(contextData.NextContextData); }
private static void ExampleLevels() { Utilities.PrintExampleBanner("Example: Levels"); /* * In this examples we describe the concept of `levels' in BFV and CKKS and the * related objects that represent them in Microsoft SEAL. * * In Microsoft SEAL a set of encryption parameters (excluding the random number * generator) is identified uniquely by a SHA-3 hash of the parameters. This * hash is called the `ParmsId' and can be easily accessed and printed at any * time. The hash will change as soon as any of the parameters is changed. * * When a SEALContext is created from a given EncryptionParameters instance, * Microsoft SEAL automatically creates a so-called `modulus switching chain', * which is a chain of other encryption parameters derived from the original set. * The parameters in the modulus switching chain are the same as the original * parameters with the exception that size of the coefficient modulus is * decreasing going down the chain. More precisely, each parameter set in the * chain attempts to remove the last coefficient modulus prime from the * previous set; this continues until the parameter set is no longer valid * (e.g., PlainModulus is larger than the remaining CoeffModulus). It is easy * to walk through the chain and access all the parameter sets. Additionally, * each parameter set in the chain has a `chain index' that indicates its * position in the chain so that the last set has index 0. We say that a set * of encryption parameters, or an object carrying those encryption parameters, * is at a higher level in the chain than another set of parameters if its the * chain index is bigger, i.e., it is earlier in the chain. * * Each set of parameters in the chain involves unique pre-computations performed * when the SEALContext is created, and stored in a SEALContext.ContextData * object. The chain is basically a linked list of SEALContext.ContextData * objects, and can easily be accessed through the SEALContext at any time. Each * node can be identified by the ParmsId of its specific encryption parameters * (PolyModulusDegree remains the same but CoeffModulus varies). */ EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV); ulong polyModulusDegree = 8192; parms.PolyModulusDegree = polyModulusDegree; /* * In this example we use a custom CoeffModulus, consisting of 5 primes of * sizes 50, 30, 30, 50, and 50 bits. Note that this is still OK according to * the explanation in `1_BFV_Basics.cs'. Indeed, * * CoeffModulus.MaxBitCount(polyModulusDegree) * * returns 218 (less than 50+30+30+50+50=210). * * Due to the modulus switching chain, the order of the 5 primes is significant. * The last prime has a special meaning and we call it the `special prime'. Thus, * the first parameter set in the modulus switching chain is the only one that * involves the special prime. All key objects, such as SecretKey, are created * at this highest level. All data objects, such as Ciphertext, can be only at * lower levels. The special modulus should be as large as the largest of the * other primes in the CoeffModulus, although this is not a strict requirement. * * special prime +---------+ | | v | CoeffModulus: { 50, 30, 30, 50, 50 } +---+ Level 4 (all keys; `key level') | | | CoeffModulus: { 50, 30, 30, 50 } +---+ Level 3 (highest `data level') | | | CoeffModulus: { 50, 30, 30 } +---+ Level 2 | | | CoeffModulus: { 50, 30 } +---+ Level 1 | | | CoeffModulus: { 50 } +---+ Level 0 (lowest level) */ parms.CoeffModulus = CoeffModulus.Create( polyModulusDegree, new int[] { 50, 30, 30, 50, 50 }); /* * In this example the PlainModulus does not play much of a role; we choose * some reasonable value. */ parms.PlainModulus = new SmallModulus(1 << 20); SEALContext context = new SEALContext(parms); Utilities.PrintParameters(context); /* * There are convenience method for accessing the SEALContext.ContextData for * some of the most important levels: * * SEALContext.KeyContextData: access to key level ContextData * SEALContext.FirstContextData: access to highest data level ContextData * SEALContext.LastContextData: access to lowest level ContextData * * We iterate over the chain and print the ParmsId for each set of parameters. */ Console.WriteLine(); Utilities.PrintLine(); Console.WriteLine("Print the modulus switching chain."); /* * First print the key level parameter information. */ SEALContext.ContextData contextData = context.KeyContextData; Console.WriteLine("----> Level (chain index): {0} ...... KeyContextData", contextData.ChainIndex); Console.WriteLine($" ParmsId: {contextData.ParmsId}"); Console.Write(" CoeffModulus primes: "); foreach (SmallModulus prime in contextData.Parms.CoeffModulus) { Console.Write($"{Utilities.ULongToString(prime.Value)} "); } Console.WriteLine(); Console.WriteLine("\\"); Console.Write(" \\--> "); /* * Next iterate over the remaining (data) levels. */ contextData = context.FirstContextData; while (null != contextData) { Console.Write($"Level (chain index): {contextData.ChainIndex}"); if (contextData.ParmsId.Equals(context.FirstParmsId)) { Console.WriteLine(" ...... FirstContextData"); } else if (contextData.ParmsId.Equals(context.LastParmsId)) { Console.WriteLine(" ...... LastContextData"); } else { Console.WriteLine(); } Console.WriteLine($" ParmsId: {contextData.ParmsId}"); Console.Write(" CoeffModulus primes: "); foreach (SmallModulus prime in contextData.Parms.CoeffModulus) { Console.Write($"{Utilities.ULongToString(prime.Value)} "); } Console.WriteLine(); Console.WriteLine("\\"); Console.Write(" \\--> "); /* * Step forward in the chain. */ contextData = contextData.NextContextData; } Console.WriteLine("End of chain reached"); Console.WriteLine(); /* * We create some keys and check that indeed they appear at the highest level. */ KeyGenerator keygen = new KeyGenerator(context); PublicKey publicKey = keygen.PublicKey; SecretKey secretKey = keygen.SecretKey; RelinKeys relinKeys = keygen.RelinKeys(); GaloisKeys galoisKeys = keygen.GaloisKeys(); Utilities.PrintLine(); Console.WriteLine("Print the parameter IDs of generated elements."); Console.WriteLine($" + publicKey: {publicKey.ParmsId}"); Console.WriteLine($" + secretKey: {secretKey.ParmsId}"); Console.WriteLine($" + relinKeys: {relinKeys.ParmsId}"); Console.WriteLine($" + galoisKeys: {galoisKeys.ParmsId}"); Encryptor encryptor = new Encryptor(context, publicKey); Evaluator evaluator = new Evaluator(context); Decryptor decryptor = new Decryptor(context, secretKey); /* * In the BFV scheme plaintexts do not carry a ParmsId, but ciphertexts do. Note * how the freshly encrypted ciphertext is at the highest data level. */ Plaintext plain = new Plaintext("1x^3 + 2x^2 + 3x^1 + 4"); Ciphertext encrypted = new Ciphertext(); encryptor.Encrypt(plain, encrypted); Console.WriteLine($" + plain: {plain.ParmsId} (not set in BFV)"); Console.WriteLine($" + encrypted: {encrypted.ParmsId}"); Console.WriteLine(); /* * `Modulus switching' is a technique of changing the ciphertext parameters down * in the chain. The function Evaluator.ModSwitchToNext always switches to the * next level down the chain, whereas Evaluator.ModSwitchTo switches to a parameter * set down the chain corresponding to a given ParmsId. However, it is impossible * to switch up in the chain. */ Utilities.PrintLine(); Console.WriteLine("Perform modulus switching on encrypted and print."); contextData = context.FirstContextData; Console.Write("----> "); while (null != contextData.NextContextData) { Console.WriteLine($"Level (chain index): {contextData.ChainIndex}"); Console.WriteLine($" ParmsId of encrypted: {contextData.ParmsId}"); Console.WriteLine(" Noise budget at this level: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); Console.WriteLine("\\"); Console.Write(" \\--> "); evaluator.ModSwitchToNextInplace(encrypted); contextData = contextData.NextContextData; } Console.WriteLine($"Level (chain index): {contextData.ChainIndex}"); Console.WriteLine($" ParmsId of encrypted: {contextData.ParmsId}"); Console.WriteLine(" Noise budget at this level: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); Console.WriteLine("\\"); Console.Write(" \\--> "); Console.WriteLine("End of chain reached"); Console.WriteLine(); /* * At this point it is hard to see any benefit in doing this: we lost a huge * amount of noise budget (i.e., computational power) at each switch and seemed * to get nothing in return. Decryption still works. */ Utilities.PrintLine(); Console.WriteLine("Decrypt still works after modulus switching."); decryptor.Decrypt(encrypted, plain); Console.WriteLine($" + Decryption of encrypted: {plain} ...... Correct."); Console.WriteLine(); /* * However, there is a hidden benefit: the size of the ciphertext depends * linearly on the number of primes in the coefficient modulus. Thus, if there * is no need or intention to perform any further computations on a given * ciphertext, we might as well switch it down to the smallest (last) set of * parameters in the chain before sending it back to the secret key holder for * decryption. * * Also the lost noise budget is actually not as issue at all, if we do things * right, as we will see below. * * First we recreate the original ciphertext and perform some computations. */ Console.WriteLine("Computation is more efficient with modulus switching."); Utilities.PrintLine(); Console.WriteLine("Compute the fourth power."); encryptor.Encrypt(plain, encrypted); Console.WriteLine(" + Noise budget before squaring: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); evaluator.SquareInplace(encrypted); evaluator.RelinearizeInplace(encrypted, relinKeys); Console.WriteLine(" + Noise budget after squaring: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); /* * From the print-out we see that the noise budget after these computations is * just slightly below the level we would have in a fresh ciphertext after one * modulus switch (135 bits). Surprisingly, in this case modulus switching has * no effect at all on the noise budget. */ evaluator.ModSwitchToNextInplace(encrypted); Console.WriteLine(" + Noise budget after modulus switching: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); /* * This means that there is no harm at all in dropping some of the coefficient * modulus after doing enough computations. In some cases one might want to * switch to a lower level slightly earlier, actually sacrificing some of the * noise budget in the process, to gain computational performance from having * smaller parameters. We see from the print-out that the next modulus switch * should be done ideally when the noise budget is down to around 81 bits. */ evaluator.SquareInplace(encrypted); evaluator.RelinearizeInplace(encrypted, relinKeys); Console.WriteLine(" + Noise budget after squaring: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); evaluator.ModSwitchToNextInplace(encrypted); Console.WriteLine(" + Noise budget after modulus switching: {0} bits", decryptor.InvariantNoiseBudget(encrypted)); /* * At this point the ciphertext still decrypts correctly, has very small size, * and the computation was as efficient as possible. Note that the decryptor * can be used to decrypt a ciphertext at any level in the modulus switching * chain. */ decryptor.Decrypt(encrypted, plain); Console.WriteLine(" + Decryption of fourth power (hexadecimal) ...... Correct."); Console.WriteLine($" {plain}"); Console.WriteLine(); /* * In BFV modulus switching is not necessary and in some cases the user might * not want to create the modulus switching chain, except for the highest two * levels. This can be done by passing a bool `false' to SEALContext constructor. */ context = new SEALContext(parms, expandModChain: false); /* * We can check that indeed the modulus switching chain has been created only * for the highest two levels (key level and highest data level). The following * loop should execute only once. */ Console.WriteLine("Optionally disable modulus switching chain expansion."); Utilities.PrintLine(); Console.WriteLine("Print the modulus switching chain."); Console.Write("----> "); for (contextData = context.KeyContextData; null != contextData; contextData = contextData.NextContextData) { Console.WriteLine($"Level (chain index): {contextData.ChainIndex}"); Console.WriteLine($" ParmsId of encrypted: {contextData.ParmsId}"); Console.Write(" CoeffModulus primes: "); foreach (SmallModulus prime in contextData.Parms.CoeffModulus) { Console.Write($"{Utilities.ULongToString(prime.Value)} "); } Console.WriteLine(); Console.WriteLine("\\"); Console.Write(" \\--> "); } Console.WriteLine("End of chain reached"); Console.WriteLine(); /* * It is very important to understand how this example works since in the CKKS * scheme modulus switching has a much more fundamental purpose and the next * examples will be difficult to understand unless these basic properties are * totally clear. */ }