Пример #1
0
        public void DecodeTest()
        {
            EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV)
            {
                PlainModulus = new SmallModulus(1024)
            };
            SEALContext    context = SEALContext.Create(parms);
            IntegerEncoder encoder = new IntegerEncoder(context);

            Plaintext plain = new Plaintext("0x^5 + 1x^4 + 1x^3 + 1x^1 + 0");

            Assert.AreEqual(6ul, plain.CoeffCount);

            ulong resultU64 = encoder.DecodeUInt64(plain);

            Assert.AreEqual(26UL, resultU64);

            long resultI64 = encoder.DecodeInt64(plain);

            Assert.AreEqual(26L, resultI64);

            uint resultU32 = encoder.DecodeUInt32(plain);

            Assert.AreEqual(26U, resultU32);

            int resultI32 = encoder.DecodeInt32(plain);

            Assert.AreEqual(26, resultI32);

            BigUInt bui = encoder.DecodeBigUInt(plain);

            Assert.IsNotNull(bui);
            Assert.AreEqual(0, bui.CompareTo(26ul));
        }
Пример #2
0
        public void DecodeTest()
        {
            IntegerEncoder encoder = new IntegerEncoder(GlobalContext.BFVContext);

            Plaintext plain = new Plaintext("0x^5 + 1x^4 + 1x^3 + 1x^1 + 0");

            Assert.AreEqual(6ul, plain.CoeffCount);

            ulong resultU64 = encoder.DecodeUInt64(plain);

            Assert.AreEqual(26UL, resultU64);

            long resultI64 = encoder.DecodeInt64(plain);

            Assert.AreEqual(26L, resultI64);

            uint resultU32 = encoder.DecodeUInt32(plain);

            Assert.AreEqual(26U, resultU32);

            int resultI32 = encoder.DecodeInt32(plain);

            Assert.AreEqual(26, resultI32);

            BigUInt bui = encoder.DecodeBigUInt(plain);

            Assert.IsNotNull(bui);
            Assert.AreEqual(0, bui.CompareTo(26ul));
        }
Пример #3
0
        static void Main(string[] args)
        {
            int   votersCount = 10000;
            ulong keysize     = 2048;

            int[] votes = createSampleVotes(votersCount, 1);
#if (TEST)
            Console.WriteLine("votes=[{0}]", string.Join(", ", votes));
#endif
            Console.WriteLine("Sum of all votes = {0}", votes.Sum());

            SEALContext    context = createContext(keysize);
            IntegerEncoder encoder = new IntegerEncoder(context);
            KeyGenerator   keygen  = new KeyGenerator(context);

            PublicKey publicKey = keygen.PublicKey;
            SecretKey secretKey = keygen.SecretKey;

            Encryptor encryptor = new Encryptor(context, publicKey);
            Evaluator evaluator = new Evaluator(context);
            Decryptor decryptor = new Decryptor(context, secretKey);

            Ciphertext encryptedTotal = new Ciphertext();
            encryptor.Encrypt(encoder.Encode(0), encryptedTotal);

            Ciphertext encrypted = new Ciphertext();
            Console.WriteLine("-----------------------------------");
            Console.WriteLine("Encoding the vote values ... ");



            Stopwatch sw = new Stopwatch();

            sw.Start();
            for (int i = 0; i < votes.Length; i++)
            {
                Plaintext plain = encoder.Encode(votes[i]);
                encryptor.Encrypt(plain, encrypted);
#if (TEST)
                Console.WriteLine($"Noise budget in encrypted: {decryptor.InvariantNoiseBudget(encrypted)} bits");

                Console.WriteLine($"Encoded {votes[i]} as polynomial {plain.ToString()}");
#endif
                evaluator.AddInplace(encryptedTotal, encrypted);
            }
            sw.Stop();
            Console.WriteLine("Elapsed={0}", sw.Elapsed);
            Console.WriteLine("Done");

            Console.WriteLine("-----------------------------------");
            Plaintext plainResult = new Plaintext();
            decryptor.Decrypt(encryptedTotal, plainResult);
            Console.Write($"Decrypting the result polynomial {plainResult.ToString()} ... ");
            Console.WriteLine("Done");

            Console.WriteLine("-----------------------------------");
            Console.WriteLine($"Decoded result: {encoder.DecodeInt32(plainResult)}");
            Console.ReadLine();
        }
Пример #4
0
        public int DecryptInt(Ciphertext EncryptedInteger)
        {
            //Takes in an encrypted Integer and Decrypts then Decodes to Int32.
            Plaintext plaintextOutput = new Plaintext();

            decryptor.Decrypt(EncryptedInteger, plaintextOutput);
            //Decode the decrypted output.
            int intDecoded;

            intDecoded = encoder.DecodeInt32(plaintextOutput);

            return(intDecoded);
        }
Пример #5
0
        public static void ExampleBasics()
        {
            PrintExampleBanner("Example: Basics");

            /*
             * In this example we demonstrate using some of the basic arithmetic operations on integers.
             *
             * SEAL uses the Fan-Vercauteren (FV) homomorphic encryption scheme. We refer to
             * https://eprint.iacr.org/2012/144 for full details on how the FV scheme works.
             */

            // Create encryption parameters.
            var parms = new EncryptionParameters();

            /*
             * First choose the polynomial modulus. This must be a power-of-2 cyclotomic polynomial,
             * i.e. a polynomial of the form "1x^(power-of-2) + 1". We recommend using polynomials of
             * degree at least 1024.
             */
            parms.SetPolyModulus("1x^2048 + 1");

            /*
             * Next we choose the coefficient modulus. SEAL comes with default values for the coefficient
             * modulus for some of the most reasonable choices of PolyModulus. They are as follows:
             *
             * /----------------------------------------------------------------------\
             | PolyModulus  | default CoeffModulus                       | security |
             | -------------|--------------------------------------------|----------|
             | 1x^2048 + 1  | 2^60 - 2^14 + 1 (60 bits)                  | 119 bit  |
             | 1x^4096 + 1  | 2^116 - 2^18 + 1 (116 bits)                | 122 bit  |
             | 1x^8192 + 1  | 2^226 - 2^26 + 1 (226 bits)                | 124 bit  |
             | 1x^16384 + 1 | 2^435 - 2^33 + 1 (435 bits)                | 130 bit  |
             | 1x^32768 + 1 | 2^889 - 2^54 - 2^53 - 2^52 + 1 (889 bits)  | 127 bit  |
             \----------------------------------------------------------------------/
             |
             | These can be conveniently accessed using ChooserEvaluator.DefaultParameterOptions, which returns
             | the above list of options as a Dictionary, keyed by the degree of the polynomial modulus. The security
             | levels are estimated based on https://eprint.iacr.org/2015/046 and https://eprint.iacr.org/2017/047.
             | We strongly recommend that the user consult an expert in the security of RLWE-based cryptography to
             | estimate the security of a particular choice of parameters.
             |
             | The user can also easily choose their custom coefficient modulus. For best performance, it should
             | be a prime of the form 2^A - B, where B is congruent to 1 modulo 2*degree(PolyModulus), and as small
             | as possible. Roughly speaking, When the rest of the parameters are held fixed, increasing CoeffModulus
             | decreases the security level. Thus we would not recommend using a value for CoeffModulus much larger
             | than those listed above (the defaults). In general, we highly recommend the user to consult with an expert
             | in the security of RLWE-based cryptography when selecting their parameters to ensure an appropriate level
             | of security.
             |
             | The size of CoeffModulus affects the total noise budget that a freshly encrypted ciphertext has. More
             | precisely, every ciphertext starts with a certain amount of noise budget, which is consumed in homomorphic
             | operations - in particular in multiplication. Once the noise budget reaches 0, the ciphertext becomes
             | impossible to decrypt. The total noise budget in a freshly encrypted ciphertext is very roughly given by
             | log2(CoeffModulus/PlainModulus), so increasing coeff_modulus will allow the user to perform more
             | homomorphic operations on the ciphertexts without corrupting them. However, we must again warn that
             | increasing CoeffModulus has a strong negative effect on the security level.
             */
            parms.SetCoeffModulus(ChooserEvaluator.DefaultParameterOptions[2048]);

            /*
             * Now we set the plaintext modulus. This can be any positive integer, even though here we take it to be a
             * power of two. A larger plaintext modulus causes the noise to grow faster in homomorphic multiplication,
             * and also lowers the maximum amount of noise in ciphertexts that the system can tolerate (see above).
             * On the other hand, a larger plaintext modulus typically allows for better homomorphic integer arithmetic,
             * although this depends strongly on which encoder is used to encode integers into plaintext polynomials.
             */
            parms.SetPlainModulus(1 << 8);

            /*
             * Once all parameters are set, we need to call EncryptionParameters::validate(), which evaluates the
             * properties of the parameters, their validity for homomorphic encryption, and performs some important
             * pre-computation.
             */
            parms.Validate();

            /*
             * Plaintext elements in the FV scheme are polynomials (represented by the Plaintext class) with coefficients
             * integers modulo PlainModulus. To encrypt for example integers instead, one must use an "encoding scheme",
             * i.e. a specific way of representing integers as such polynomials. SEAL comes with a few basic encoders:
             *
             * IntegerEncoder:
             * Given an integer base b, encodes integers as plaintext polynomials in the following way. First, a base-b
             * expansion of the integer is computed. This expansion uses a "balanced" set of representatives of integers
             * modulo b as the coefficients. Namely, when b is off the coefficients are integers between -(b-1)/2 and
             * (b-1)/2. When b is even, the integers are between -b/2 and (b-1)/2, except when b is two and the usual
             * binary expansion is used (coefficients 0 and 1). Decoding amounts to evaluating the polynomial at x=b.
             * For example, if b=2, the integer 26 = 2^4 + 2^3 + 2^1 is encoded as the polynomial 1x^4 + 1x^3 + 1x^1.
             * When b=3, 26 = 3^3 - 3^0 is encoded as the polynomial 1x^3 - 1. In reality, coefficients of polynomials
             * are always unsigned integers, and in this case are stored as their smallest non-negative representatives
             * modulo plain_modulus. To create an integer encoder with a base b, use IntegerEncoder(PlainModulus, b).
             * If no b is given to the constructor, the default value of b=2 is used.
             *
             * FractionalEncoder:
             * Encodes fixed-precision rational numbers as follows. First expand the number in a given base b, possibly
             * truncating an infinite fractional part to finite precision, e.g. 26.75 = 2^4 + 2^3 + 2^1 + 2^(-1) + 2^(-2)
             * when b=2. For the sake of the example, suppose PolyModulus is 1x^1024 + 1. Next represent the integer part
             * of the number in the same way as in IntegerEncoder (with b=2 here). Finally, represent the fractional part
             * in the leading coefficients of the polynomial, but when doing so invert the signs of the coefficients. So
             * in this example we would represent 26.75 as the polynomial -1x^1023 - 1x^1022 + 1x^4 + 1x^3 + 1x^1. The
             * negative coefficients of the polynomial will again be represented as their negatives modulo PlainModulus.
             *
             * PolyCRTBuilder:
             * If PolyModulus is 1x^N + 1, PolyCRTBuilder allows "batching" of N plaintext integers modulo plain_modulus
             * into one plaintext polynomial, where homomorphic operations can be carried out very efficiently in a SIMD
             * manner by operating on such a "composed" plaintext or ciphertext polynomials. For full details on this very
             * powerful technique we recommend https://eprint.iacr.org/2012/565.pdf and https://eprint.iacr.org/2011/133.
             *
             * A crucial fact to understand is that when homomorphic operations are performed on ciphertexts, they will
             * carry over to the underlying plaintexts, and as a result of additions and multiplications the coefficients
             * in the plaintext polynomials will increase from what they originally were in freshly encoded polynomials.
             * This becomes a problem when the coefficients reach the size of PlainModulus, in which case they will get
             * automatically reduced modulo PlainModulus, and might render the underlying plaintext polynomial impossible
             * to be correctly decoded back into an integer or rational number. Therefore, it is typically crucial to
             * have a good sense of how large the coefficients will grow in the underlying plaintext polynomials when
             * homomorphic computations are carried out on the ciphertexts, and make sure that PlainModulus is chosen to
             * be at least as large as this number.
             *
             * Here we choose to create an IntegerEncoder with base b=2.
             */
            var encoder = new IntegerEncoder(parms.PlainModulus);

            // Encode two integers as polynomials.
            const int value1   = 5;
            const int value2   = -7;
            var       encoded1 = encoder.Encode(value1);
            var       encoded2 = encoder.Encode(value2);

            Console.WriteLine("Encoded {0} as polynomial {1}", value1, encoded1);
            Console.WriteLine("Encoded {0} as polynomial {1}", value2, encoded2);

            // Generate keys.
            Console.WriteLine("Generating keys ...");
            var generator = new KeyGenerator(parms);

            generator.Generate();
            Console.WriteLine("... key generation completed");
            var publicKey = generator.PublicKey;
            var secretKey = generator.SecretKey;

            // Encrypt values.
            Console.WriteLine("Encrypting values...");
            var encryptor  = new Encryptor(parms, publicKey);
            var encrypted1 = encryptor.Encrypt(encoded1);
            var encrypted2 = encryptor.Encrypt(encoded2);

            // Perform arithmetic on encrypted values.
            Console.WriteLine("Performing arithmetic on ecrypted numbers ...");
            var evaluator = new Evaluator(parms);

            Console.WriteLine("Performing homomorphic negation ...");
            var encryptedNegated1 = evaluator.Negate(encrypted1);

            Console.WriteLine("Performing homomorphic addition ...");
            var encryptedSum = evaluator.Add(encrypted1, encrypted2);

            Console.WriteLine("Performing homomorphic subtraction ...");
            var encryptedDiff = evaluator.Sub(encrypted1, encrypted2);

            Console.WriteLine("Performing homomorphic multiplication ...");
            var encryptedProduct = evaluator.Multiply(encrypted1, encrypted2);

            // Decrypt results.
            Console.WriteLine("Decrypting results ...");
            var decryptor         = new Decryptor(parms, secretKey);
            var decrypted1        = decryptor.Decrypt(encrypted1);
            var decrypted2        = decryptor.Decrypt(encrypted2);
            var decryptedNegated1 = decryptor.Decrypt(encryptedNegated1);
            var decryptedSum      = decryptor.Decrypt(encryptedSum);
            var decryptedDiff     = decryptor.Decrypt(encryptedDiff);
            var decryptedProduct  = decryptor.Decrypt(encryptedProduct);

            // Decode results.
            var decoded1        = encoder.DecodeInt32(decrypted1);
            var decoded2        = encoder.DecodeInt32(decrypted2);
            var decodedNegated1 = encoder.DecodeInt32(decryptedNegated1);
            var decodedSum      = encoder.DecodeInt32(decryptedSum);
            var decodedDiff     = encoder.DecodeInt32(decryptedDiff);
            var decodedProduct  = encoder.DecodeInt32(decryptedProduct);

            // Display results.
            Console.WriteLine("Original = {0}; after encryption/decryption = {1}", value1, decoded1);
            Console.WriteLine("Original = {0}; after encryption/decryption = {1}", value2, decoded2);
            Console.WriteLine("Encrypted negate of {0} = {1}", value1, decodedNegated1);
            Console.WriteLine("Encrypted addition of {0} and {1} = {2}", value1, value2, decodedSum);
            Console.WriteLine("Encrypted subtraction of {0} and {1} = {2}", value1, value2, decodedDiff);
            Console.WriteLine("Encrypted multiplication of {0} and {1} = {2}", value1, value2, decodedProduct);

            // How much noise budget did we use in these operations?
            Console.WriteLine("Noise budget in encryption of {0}: {1} bits", value1, decryptor.InvariantNoiseBudget(encrypted1));
            Console.WriteLine("Noise budget in encryption of {0}: {1} bits", value2, decryptor.InvariantNoiseBudget(encrypted2));
            Console.WriteLine("Noise budget in sum: {0} bits", decryptor.InvariantNoiseBudget(encryptedSum));
            Console.WriteLine("Noise budget in product: {0} bits", decryptor.InvariantNoiseBudget(encryptedProduct));
        }
Пример #6
0
        /*
         * In `1_BFV_Basics.cs' we showed how to perform a very simple computation using the
         * BFV scheme. The computation was performed modulo the PlainModulus parameter, and
         * utilized only one coefficient from a BFV plaintext polynomial. This approach has
         * two notable problems:
         *
         *  (1) Practical applications typically use integer or real number arithmetic,
         *      not modular arithmetic;
         *  (2) We used only one coefficient of the plaintext polynomial. This is really
         *      wasteful, as the plaintext polynomial is large and will in any case be
         *      encrypted in its entirety.
         *
         * For (1), one may ask why not just increase the PlainModulus parameter until no
         * overflow occurs, and the computations behave as in integer arithmetic. The problem
         * is that increasing PlainModulus increases noise budget consumption, and decreases
         * the initial noise budget too.
         *
         * In these examples we will discuss other ways of laying out data into plaintext
         * elements (encoding) that allow more computations without data type overflow, and
         * can allow the full plaintext polynomial to be utilized.
         */
        private static void ExampleIntegerEncoder()
        {
            Utilities.PrintExampleBanner("Example: Encoders / Integer Encoder");

            /*
             * [IntegerEncoder] (For BFV scheme only)
             *
             * The IntegerEncoder encodes integers to BFV plaintext polynomials as follows.
             * First, a binary expansion of the integer is computed. Next, a polynomial is
             * created with the bits as coefficients. For example, the integer
             *
             *  26 = 2^4 + 2^3 + 2^1
             *
             * is encoded as the polynomial 1x^4 + 1x^3 + 1x^1. Conversely, plaintext
             * polynomials are decoded by evaluating them at x=2. For negative numbers the
             * IntegerEncoder simply stores all coefficients as either 0 or -1, where -1 is
             * represented by the unsigned integer PlainModulus - 1 in memory.
             *
             * Since encrypted computations operate on the polynomials rather than on the
             * encoded integers themselves, the polynomial coefficients will grow in the
             * course of such computations. For example, computing the sum of the encrypted
             * encoded integer 26 with itself will result in an encrypted polynomial with
             * larger coefficients: 2x^4 + 2x^3 + 2x^1. Squaring the encrypted encoded
             * integer 26 results also in increased coefficients due to cross-terms, namely,
             *
             *  (1x^4 + 1x^3 + 1x^1)^2 = 1x^8 + 2x^7 + 1x^6 + 2x^5 + 2x^4 + 1x^2;
             *
             * further computations will quickly increase the coefficients much more.
             * Decoding will still work correctly in this case (evaluating the polynomial
             * at x=2), but since the coefficients of plaintext polynomials are really
             * integers modulo plain_modulus, implicit reduction modulo plain_modulus may
             * yield unexpected results. For example, adding 1x^4 + 1x^3 + 1x^1 to itself
             * plain_modulus many times will result in the constant polynomial 0, which is
             * clearly not equal to 26 * plain_modulus. It can be difficult to predict when
             * such overflow will take place especially when computing several sequential
             * multiplications.
             *
             * The IntegerEncoder is easy to understand and use for simple computations,
             * and can be a good tool to experiment with for users new to Microsoft SEAL.
             * However, advanced users will probably prefer more efficient approaches,
             * such as the BatchEncoder or the CKKSEncoder.
             */
            EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);
            ulong polyModulusDegree    = 4096;

            parms.PolyModulusDegree = polyModulusDegree;
            parms.CoeffModulus      = CoeffModulus.BFVDefault(polyModulusDegree);

            /*
             * There is no hidden logic behind our choice of the plain_modulus. The only
             * thing that matters is that the plaintext polynomial coefficients will not
             * exceed this value at any point during our computation; otherwise the result
             * will be incorrect.
             */
            parms.PlainModulus = new SmallModulus(512);
            SEALContext context = new SEALContext(parms);

            Utilities.PrintParameters(context);
            Console.WriteLine();

            KeyGenerator keygen    = new KeyGenerator(context);
            PublicKey    publicKey = keygen.PublicKey;
            SecretKey    secretKey = keygen.SecretKey;
            Encryptor    encryptor = new Encryptor(context, publicKey);
            Evaluator    evaluator = new Evaluator(context);
            Decryptor    decryptor = new Decryptor(context, secretKey);

            /*
             * We create an IntegerEncoder.
             */
            IntegerEncoder encoder = new IntegerEncoder(context);

            /*
             * First, we encode two integers as plaintext polynomials. Note that encoding
             * is not encryption: at this point nothing is encrypted.
             */
            int       value1 = 5;
            Plaintext plain1 = encoder.Encode(value1);

            Utilities.PrintLine();
            Console.WriteLine($"Encode {value1} as polynomial {plain1} (plain1),");

            int       value2 = -7;
            Plaintext plain2 = encoder.Encode(value2);

            Console.WriteLine(new string(' ', 13)
                              + $"Encode {value2} as polynomial {plain2} (plain2),");

            /*
             * Now we can encrypt the plaintext polynomials.
             */
            Ciphertext encrypted1 = new Ciphertext();
            Ciphertext encrypted2 = new Ciphertext();

            Utilities.PrintLine();
            Console.WriteLine("Encrypt plain1 to encrypted1 and plain2 to encrypted2.");
            encryptor.Encrypt(plain1, encrypted1);
            encryptor.Encrypt(plain2, encrypted2);
            Console.WriteLine("    + Noise budget in encrypted1: {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted1));
            Console.WriteLine("    + Noise budget in encrypted2: {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted2));

            /*
             * As a simple example, we compute (-encrypted1 + encrypted2) * encrypted2.
             */
            encryptor.Encrypt(plain2, encrypted2);
            Ciphertext encryptedResult = new Ciphertext();

            Utilities.PrintLine();
            Console.WriteLine("Compute encrypted_result = (-encrypted1 + encrypted2) * encrypted2.");
            evaluator.Negate(encrypted1, encryptedResult);
            evaluator.AddInplace(encryptedResult, encrypted2);
            evaluator.MultiplyInplace(encryptedResult, encrypted2);
            Console.WriteLine("    + Noise budget in encryptedResult: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedResult));

            Plaintext plainResult = new Plaintext();

            Utilities.PrintLine();
            Console.WriteLine("Decrypt encrypted_result to plain_result.");
            decryptor.Decrypt(encryptedResult, plainResult);

            /*
             * Print the result plaintext polynomial. The coefficients are not even close
             * to exceeding our plainModulus, 512.
             */
            Console.WriteLine($"    + Plaintext polynomial: {plainResult}");

            /*
             * Decode to obtain an integer result.
             */
            Utilities.PrintLine();
            Console.WriteLine("Decode plain_result.");
            Console.WriteLine("    + Decoded integer: {0} ...... Correct.",
                              encoder.DecodeInt32(plainResult));
        }
Пример #7
0
        private static void HomoExample()
        {
            EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);

            parms.PolyModulusDegree = 2048;
            parms.CoeffModulus      = DefaultParams.CoeffModulus128(polyModulusDegree: 2048);
            parms.PlainModulus      = new SmallModulus(1 << 8);
            SEALContext    context = SEALContext.Create(parms);
            IntegerEncoder encoder = new IntegerEncoder(context);

            KeyGenerator keygen = new KeyGenerator(context);

            Microsoft.Research.SEAL.PublicKey publicKey = keygen.PublicKey;
            SecretKey secretKey = keygen.SecretKey;

            Encryptor encryptor = new Encryptor(context, publicKey);
            Evaluator evaluator = new Evaluator(context);

            Decryptor decryptor = new Decryptor(context, secretKey);

            int       value1 = 5;
            Plaintext plain1 = encoder.Encode(value1);

            Console.WriteLine($"Encoded {value1} as polynomial {plain1.ToString()} (plain1)");

            int       value2 = -7;
            Plaintext plain2 = encoder.Encode(value2);

            Console.WriteLine($"Encoded {value2} as polynomial {plain2.ToString()} (plain2)");

            Ciphertext encrypted1 = new Ciphertext();
            Ciphertext encrypted2 = new Ciphertext();

            Console.Write("Encrypting plain1: ");

            encryptor.Encrypt(plain1, encrypted1);
            Console.WriteLine("Done (encrypted1)");

            Plaintext plainResult = new Plaintext();

            decryptor.Decrypt(encrypted1, plainResult);
            Console.WriteLine(encoder.DecodeInt32(plainResult));


            Console.Write("Encrypting plain2: ");
            encryptor.Encrypt(plain2, encrypted2);
            Console.WriteLine("Done (encrypted2)");



            Console.WriteLine($"Noise budget in encrypted1: {decryptor.InvariantNoiseBudget(encrypted1)} bits");
            Console.WriteLine($"Noise budget in encrypted2: {decryptor.InvariantNoiseBudget(encrypted2)} bits");

            evaluator.NegateInplace(encrypted1);
            Console.WriteLine($"Noise budget in -encrypted1: {decryptor.InvariantNoiseBudget(encrypted1)} bits");

            evaluator.AddInplace(encrypted1, encrypted2);

            Console.WriteLine($"Noise budget in -encrypted1 + encrypted2: {decryptor.InvariantNoiseBudget(encrypted1)} bits");

            evaluator.MultiplyInplace(encrypted1, encrypted2);

            Console.WriteLine($"Noise budget in (-encrypted1 + encrypted2) * encrypted2: {decryptor.InvariantNoiseBudget(encrypted1)} bits");

            plainResult = new Plaintext();
            Console.Write("Decrypting result: ");
            decryptor.Decrypt(encrypted1, plainResult);
            Console.WriteLine("Done");

            Console.WriteLine($"Plaintext polynomial: {plainResult.ToString()}");

            Console.WriteLine($"Decoded integer: {encoder.DecodeInt32(plainResult)}");
        }