Пример #1
0
        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]);
            }
        }
Пример #2
0
        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);
        }
Пример #3
0
        /// <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("\\");
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        /// <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();
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }
Пример #10
0
        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.
             */
        }