// register X - initially loaded with x
        // register X_1 - initially loaded with 1
        // W = width(a) = width(N)
        // width(b) = width(c) = width(x1) = W + 1
        // width(X) is 2 * W
        // register N - initially loaded with N
        // other registers - initially 0
        // after computation register X_1 changes and contains: [(a^x) mod N]
        // Insecure version: registers widths etc. are not checked
        public static void ExpModulo(
            this QuantumComputer comp,
            Register a,
            Register b,
            Register c,
            Register N,
            Register x1,
            Register x,
            int valueA,
            int valueN)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, c, N, x1, x, valueA, valueN };
                comp.AddParametricGate("ExpModulo", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            bool firstRegisterB = false;
            int pow_a_2 = valueA;

            for (int i = 0; i < x.Width; i++)
            {
                // finding the inversion modulo of pow_a_2
                int inv_mod = InversionModulo(pow_a_2, valueN);

                if (firstRegisterB)
                {
                    comp.CMultModulo(a, x1, c, N, b, x[i],
                        (ulong)pow_a_2, (ulong)valueN);
                    comp.InverseCMultModulo(a, b, c, N, x1, x[i],
                        (ulong)inv_mod, (ulong)valueN);
                }
                else
                {
                    comp.CMultModulo(a, b, c, N, x1, x[i],
                        (ulong)pow_a_2, (ulong)valueN);
                    comp.InverseCMultModulo(a, x1, c, N, b, x[i],
                        (ulong)inv_mod, (ulong)valueN);
                }
                pow_a_2 = (pow_a_2 * pow_a_2) % valueN;
                firstRegisterB = !firstRegisterB;
            }
        }
        // register x - initially loaded with x
        // register b - initially loaded with 0
        // after computation register b changes and contains: [(a*x) mod N]
        // Secure version: throws Exceptions if arguments are invalid
        public static void CMultModulo(
            this QuantumComputer comp,
            Register x,
            Register b,
            RegisterRef control,
            ulong valueA,
            ulong valueN)
        {
            Validate(x, b, valueN);

            Register a = comp.NewRegister(0, x.Width - 1);
            Register c = comp.NewRegister(0, x.Width);
            Register N = comp.NewRegister(valueN, x.Width - 1);

            comp.CMultModulo(a, b, c, N, x, control, valueA, valueN);

            comp.DeleteRegister(ref N);
            comp.DeleteRegister(ref c);
            comp.DeleteRegister(ref a);
        }
        /// <summary>
        /// <para>
        /// Performs (a^x) modulo N, for given integers a and N. The x (one value or a superposition) 
        /// is given in the input register x.
        /// </para>
        /// <para>
        /// After computation, the result (or results, when x stores superposition of multiple integers) is stored in 
        /// register x1.
        /// </para>
        /// <para>
        /// This method is a variant of <c>ExpModulo</c> function, which operates directly on quantum registers given as arguments.
        /// It neither allocates nor frees any additional registers. It is thus recommended, when there is a need for performing
        /// modular exponentiation repeatedly. However, this variant has strict requirements for the width of each given register
        /// and if they are not fullfilled, the method gives unexpected results.
        /// </para>
        /// </summary>
        /// <remarks>
        /// <para>
        /// There are precise requirements for the width of each register given as argument. They result from a need for carry bits,
        /// overflow flag and a requirement for ensuring that the operation is inversible.
        /// </para>
        /// <para>
        /// Let <b>WIDTH</b> equals the number of bits required to store N.
        /// </para>
        /// <para>
        /// The width of x register must equal <b>2 * WIDTH</b>. This value results from the requirements of Peter Shor's
        /// algorithm. Such a width ensures that the probability of getting the right result will be enough high.
        /// </para>
        /// </remarks>
        /// <param name="comp">The QuantumComputer instance.</param>
        /// <param name="a">Accumulator register. Its initial value must be 0. Its width must equal <b>WIDTH</b> (See Remarks).</param>
        /// <param name="b">Helper register. Its initial value must be 0. Its width must equal <b>WIDTH + 1</b> (See Remarks).</param>
        /// <param name="c">Register for storing carry bits. Its initial value must be 0. Its width must equal <b>WIDTH + 1</b> (See Remarks).</param>
        /// <param name="N">Register for N. Its initial value must equal N. Its width must equal <b>WIDTH</b> (See Remarks).</param>
        /// <param name="x1">Output register. Its initial value must equal 1. Its width must equal <b>WIDTH + 1</b> (See Remarks).</param>
        /// <param name="x">Register for x. Its initial value could be any integer or a superposition of multiple integers. Its width must equal <b>2 * WIDTH</b> (See Remarks).</param>
        /// <param name="valueA">Integer value of a. (For computing (a^x) modulo N).</param>
        /// <param name="valueN">Integer value of N. (For computing (a^x) modulo N).</param>
        public static void ExpModulo(
            this QuantumComputer comp,
            Register a,
            Register b,
            Register c,
            Register N,
            Register x1,
            Register x,
            int valueA,
            int valueN)
        {
            bool firstRegisterB = false;
            int pow_a_2 = valueA;

            for (int i = 0; i < x.Width; i++)
            {
                // finding the inversion modulo of pow_a_2
                int inv_mod = InversionModulo(pow_a_2, valueN);

                if (firstRegisterB)
                {
                    comp.CMultModulo(a, x1, c, N, b, x[i],
                        (ulong)pow_a_2, (ulong)valueN);
                    comp.InverseCMultModulo(a, b, c, N, x1, x[i],
                        (ulong)inv_mod, (ulong)valueN);
                }
                else
                {
                    comp.CMultModulo(a, b, c, N, x1, x[i],
                        (ulong)pow_a_2, (ulong)valueN);
                    comp.InverseCMultModulo(a, x1, c, N, b, x[i],
                        (ulong)inv_mod, (ulong)valueN);
                }
                pow_a_2 = (pow_a_2 * pow_a_2) % valueN;
                firstRegisterB = !firstRegisterB;
            }
        }
        // register x - initially loaded with x
        // register b - initially loaded with 0
        // after computation register b changes and contains: [(a*x) mod N]
        // Secure version: throws Exceptions if arguments are invalid
        public static void CMultModulo(
            this QuantumComputer comp,
            Register x,
            Register b,
            RegisterRef control,
            ulong valueA,
            ulong valueN)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, x, b, control, valueA, valueN };
                comp.AddParametricGate("CMultModulo", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(x, b, valueN);

            Register a = comp.NewRegister(0, x.Width - 1);
            Register c = comp.NewRegister(0, x.Width);
            Register N = comp.NewRegister(valueN, x.Width - 1);

            comp.CMultModulo(a, b, c, N, x, control, valueA, valueN);

            comp.DeleteRegister(ref N);
            comp.DeleteRegister(ref c);
            comp.DeleteRegister(ref a);
        }