public static void AddModuloQFTPhi(this QuantumComputer comp, ulong a, ulong N, RegisterRef ctrl, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, ctrl, b, controls };
                comp.AddParametricGate("AddModuloQFTPhi", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            comp.AddQFTPhi(a, b, controls);
            comp.InverseAddQFTPhi(N, b);

            comp.InverseQFT(b);
            comp.CNot(ctrl, b[b.Width - 1]);
            comp.QFT(b);

            comp.AddQFTPhi(N, b, ctrl);
            comp.InverseAddQFTPhi(a, b, controls);

            comp.InverseQFT(b);
            comp.SigmaX(b[b.Width - 1]);
            comp.CNot(ctrl, b[b.Width - 1]);
            comp.SigmaX(b[b.Width - 1]);
            comp.QFT(b);

            comp.AddQFTPhi(a, b, controls);
        }
        // Add(a, b, 0) -> (a, a+b, 0)
        // Registers a, b and c must not overlap
        // Registers a and b have the same width
        // Register c is used for storing carries and must be minimum one bit wider than register a (or b)
        // Initial value of c must be 0
        public static void Add(this QuantumComputer comp,
            Register a, Register b, Register c)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, c };
                comp.AddParametricGate("Add", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            int width = a.Width;
            int i = 0;
            for (; i < width - 1; i++)
            {
                comp.Carry(c[i], a[i], b[i], c[i + 1]);
            }
            comp.Carry(c[i], a[i], b[i], b[i + 1]);

            comp.CNot(b[i], a[i]);
            comp.Sum(c[i], a[i], b[i]);
            i--;
            for (; i >= 0; i--)
            {
                comp.InverseCarry(c[i], a[i], b[i], c[i + 1]);
                comp.Sum(c[i], a[i], b[i]);
            }
        }
        public static void AddQFTPhi(this QuantumComputer comp, Register a, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, controls };
                comp.AddParametricGate("AddQFTPhi", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(a, b);

            for (int j = b.Width - 1; j >= 0; j--)
            {
                for (int i = j; i >= 0; i--)
                {
                    List<RegisterRef> list = controls.ToList<RegisterRef>();
                    list.Add(a[i]);
                    RegisterRef[] controls2 = list.ToArray();
                    comp.CPhaseShift(Math.Abs(j-i), b[j], controls2);
                }
            }
        }
        public static void ControlledUaGate(this QuantumComputer comp, ulong a, ulong N, RegisterRef ctrl, Register x, Register reg0, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, ctrl, x, reg0, control };
                comp.AddParametricGate("ControlledUaGate", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(x, N);

            int? invA = Quantum.Utils.InversionModulo((int)a, (int)N);

            if (invA == null)
            {
                throw new ArgumentException("No inversion for specified a = " + a);
            }

            comp.MultModuloQFT(a, N, ctrl, x, reg0, control);
            comp.Swap(x, reg0, control);
            comp.InverseMultModuloQFT((ulong)invA, N, ctrl, x, reg0, control);
        }
 public static void InverseLoadNumber(this QuantumComputer comp, Register target, ulong number, params RegisterRef[] controlBits)
 {
     if (comp.Group)
     {
         object[] parameters = new object[] { comp, target, number, controlBits };
         comp.AddParametricGate("InverseLoadNumber", parameters);
         return;
     }
     else
     {
         comp.Group = true;
     }
     comp.LoadNumber(target, number, controlBits);
 }
        public static void InverseReverse(this QuantumComputer comp, Register a)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a };
                comp.AddParametricGate("InverseReverse", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            comp.Reverse(a);
        }
        public static void InverseSwap(this QuantumComputer comp, Register r1, Register r2, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, r1, r2, control };
                comp.AddParametricGate("InverseSwap", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            comp.Swap(r1, r2, control);
        }
        public static void InverseWalsh(this QuantumComputer comp, Register register)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, register };
                comp.AddParametricGate("InverseWalsh", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            comp.Walsh(register);
        }
        // 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;
            }
        }
        public static void AddModuloQFT(this QuantumComputer comp, ulong a, ulong N, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, b, controls };
                comp.AddParametricGate("AddModuloQFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register ctrl = comp.NewRegister(0, 1);
            comp.AddModuloQFT(a, N, ctrl, b, controls);
        }
        public static void InverseMultModuloQFT(this QuantumComputer comp, ulong a, ulong N, Register x, Register b, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, x, b, control };
                comp.AddParametricGate("InverseMultModuloQFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register ctrl = comp.NewRegister(0, 1);
            comp.InverseMultModuloQFT(a, N, ctrl, x, b, control);
        }
        public static void ControlledUaGate(this QuantumComputer comp, ulong a, ulong N, Register x, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, x, control };
                comp.AddParametricGate("ControlledUaGate", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register ctrl = comp.NewRegister(0, 1);
            Register reg0 = comp.NewRegister(0, x.Width);
            comp.ControlledUaGate(a, N, ctrl, x, reg0, control);
        }
        // register x - initially loaded with x
        // register N - initially loaded with N
        // control - control bit
        // other registers - initially 0
        // after computation register B changes and contains: [(a*x) mod N]
        // Insecure version: registers widths etc. are not checked
        public static void CMultModulo(
            this QuantumComputer comp,
            Register a,
            Register b,
            Register c,
            Register N,
            Register x,
            RegisterRef control,
            ulong valueA,
            ulong valueN)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, c, N, x, control, valueA, valueN };
                comp.AddParametricGate("CMultModulo", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            ulong power2 = 1;
            for (int i = 0; i < x.Width; i++, power2 *= 2)
            {
                // loading A register with (2^i * a) mod N
                ulong toLoad = (valueA * power2) % valueN;
                comp.LoadNumber(a, toLoad, control, x[i]);

                // adding [(2^i * a) + B] modulo N
                comp.AddModulo(a, b, c, N, valueN);

                // unloading [(2^i * a) mod N] from A register
                comp.LoadNumber(a, toLoad, control, x[i]);
            }

            // if control == 0
            // then B register contains still 0
            // so we copy X register into B register
            comp.SigmaX(control);
            for (int i = 0; i < x.Width; i++)
            {
                comp.Toffoli(b[i], control, x[i]);
            }
            comp.SigmaX(control);
        }
        //Apply a walsh-hadamard transform on whole register
        public static void Walsh(this QuantumComputer comp, Register register)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, register };
                comp.AddParametricGate("Walsh", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            for (int i = 0; i < register.Width; i++)
            {
                register.Hadamard(i);
            }
        }
        public static void AddQFT(this QuantumComputer comp, ulong a, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, controls };
                comp.AddParametricGate("AddQFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(a, b);
            comp.QFT(b);
            comp.AddQFTPhi(a, b, controls);
            comp.InverseQFT(b);
        }
        // register A - initially loaded with a
        // register B - initially loaded with b
        // register C - initially 0, exactly one bit wider than A, stores overflow bit
        // register N - initially loaded with N
        // after computation in register B: (a+b) mod N
        // other registers dont change their states
        // register B must be exactly one bit wider than A to store carry bit
        // register B must be exactly one bit wider than N to store carry bit
        // registers A, N must be the same length
        // Insecure version: registers widths etc. are not checked
        public static void AddModulo(
            this QuantumComputer comp,
            Register a,
            Register b,
            Register c,
            Register N,
            ulong valueN)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, c, N, valueN };
                comp.AddParametricGate("AddModulo", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            RegisterRef carry = b[b.Width - 1];
            RegisterRef overflow = c[c.Width - 1];

            comp.Add(a, b, c);
            comp.InverseAdd(N, b, c);
            comp.SigmaX(carry);
            comp.CNot(overflow, carry);
            comp.SigmaX(carry);

            //resetting N
            comp.LoadNumber(N, valueN, overflow);

            comp.Add(N, b, c);

            // now we have [(a+b) mod N] in B register
            // next steps lead to recover the initial state of registers N and overflow bit

            //setting N back
            comp.LoadNumber(N, valueN, overflow);

            comp.InverseAdd(a, b, c);
            comp.CNot(overflow, carry);
            comp.Add(a, b, c);
        }
        // Add(a, b) -> (a, a+b)
        // Registers a and b must not overlap
        // Registers a and b have the same width
        // Initially, there is scratch register used
        public static void Add(this QuantumComputer comp, Register a, Register b)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b };
                comp.AddParametricGate("Add", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(a, b);

            Register c = comp.NewRegister(0, a.Width);
            comp.Add(a, b, c);
            comp.DeleteRegister(ref c);
        }
        // controlled loading a number into register
        // using Toffoli gates
        // if controlBits are empty, the number is loaded unconditionally
        public static void LoadNumber(this QuantumComputer comp, Register target, ulong number, params RegisterRef[] controlBits)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, target, number, controlBits };
                comp.AddParametricGate("LoadNumber", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(target, number);

            int controlLength = controlBits.Length;

            int i = 0;
            ulong tmpN = number;
            while (tmpN > 0)
            {
                int rest = (int)(tmpN % 2);
                tmpN = tmpN / 2;

                if (rest == 1)
                {
                    if (controlLength > 1)
                    {
                        comp.Toffoli(target[i], controlBits);
                    }
                    else if (controlLength > 0)
                    {
                        comp.CNot(target[i], controlBits[0]);
                    }
                    else
                    {
                        target.SigmaX(i);
                    }
                }
                i++;
            }
        }
        public static void InverseMultModuloQFT(this QuantumComputer comp, ulong a, ulong N, RegisterRef ctrl, Register x, Register b, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, N, ctrl, x, b, control };
                comp.AddParametricGate("InverseMultModuloQFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(a, b, N);
            comp.QFT(b);

            for (int i = x.Width - 1; i >= 0; i--)
            {
                comp.InverseAddModuloQFTPhi(((((ulong)1 << i) * a) % N), N, ctrl, b, x[i], control);
            }

            comp.InverseQFT(b);
        }
        public static void InverseQFT(this QuantumComputer comp, Register register)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, register };
                comp.AddParametricGate("InverseQFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            int width = register.Width;
            for (int i = 0; i < width; i++)
            {
                register.Hadamard(i);

                for (int j = i + 1; j < width; j++)
                {
                    register.InverseCPhaseShift(i - j, i, j);
                }
            }
        }
        public static void Reverse(this QuantumComputer comp, Register a)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a };
                comp.AddParametricGate("Reverse", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            for (int i = 0; i < a.Width / 2; i++)
            {
                comp.Swap(a[i], a[a.Width - 1 - i]);
            }
        }
        public static void Swap(this QuantumComputer comp, Register r1, Register r2, RegisterRef control)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, r1, r2, control };
                comp.AddParametricGate("Swap", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(r1, r2);

            Register root = comp.GetRootRegister(r1, r2, control);
            int target1 = r1.OffsetToRoot;
            int target2 = r2.OffsetToRoot;
            int ctrl = control.OffsetToRoot;

            for (int i = 0; i < r1.Width; i++)
            {
                //comp.Toffoli(root[target1 + i], root[target2 + i], root[ctrl]);
                //comp.Toffoli(root[target2 + i], root[target1 + i], root[ctrl]);
                //comp.Toffoli(root[target1 + i], root[target2 + i], root[ctrl]);
                root.Toffoli(target1 + i, target2 + i, ctrl);
                root.Toffoli(target2 + i, target1 + i, ctrl);
                root.Toffoli(target1 + i, target2 + i, ctrl);
            }
        }
        // Swap two bits
        public static void Swap(this QuantumComputer comp, RegisterRef r1, RegisterRef r2)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, r1, r2 };
                comp.AddParametricGate("Swap", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register root = comp.GetRootRegister(r1, r2);
            int target1 = r1.OffsetToRoot;
            int target2 = r2.OffsetToRoot;

            root.CNot(target1, target2);
            root.CNot(target2, target1);
            root.CNot(target1, target2);
        }
        public static void InverseSum(this QuantumComputer comp,
            RegisterRef refA, RegisterRef refB, RegisterRef refTarget)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, refA, refB, refTarget };
                comp.AddParametricGate("Sum", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            comp.Sum(refA, refB, refTarget);
        }
        //public static void QFT(this QuantumComputer comp, Register register)
        //{
        //    int width = register.Width;
        //    for (int i = width - 1; i >= 0; i--)
        //    {
        //        register.Hadamard(i);
        //        for (int j = i - 1; j >= 0; j--)
        //        {
        //            register.CPhaseShift(i - j, i, j);
        //        }
        //    }
        //}
        //public static void InverseQFT(this QuantumComputer comp, Register register)
        //{
        //    int width = register.Width;
        //    for (int i = 0; i < width; i++)
        //    {
        //        for (int j = 0; j < i; j++)
        //        {
        //            register.InverseCPhaseShift(i - j, i, j);
        //        }
        //        register.Hadamard(i);
        //    }
        //}
        public static void QFT(this QuantumComputer comp, Register register)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, register };
                comp.AddParametricGate("QFT", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            int width = register.Width;
            for (int i = width - 1; i >= 0; i--)
            {
                for (int j = width - 1; j > i; j--)
                {
                    register.CPhaseShift(i - j, i, j);
                }
                register.Hadamard(i);
            }
        }
        public static void Sum(this QuantumComputer comp,
            RegisterRef refA, RegisterRef refB, RegisterRef refTarget)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, refA, refB, refTarget };
                comp.AddParametricGate("Sum", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register root = comp.GetRootRegister(refA, refB, refTarget);
            int a = refA.OffsetToRoot;
            int b = refB.OffsetToRoot;
            int target = refTarget.OffsetToRoot;
            root.CNot(target, a);
            root.CNot(target, b);
        }
        public static void InverseAddQFTPhi(this QuantumComputer comp, ulong a, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, controls };
                comp.AddParametricGate("InverseAddQFTPhi", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            bool[] aBin = Quantum.Utils.getBinaryRepresentation(a, b.Width);
            for (int j = 0; j < b.Width; j++)
            {
                for (int i = 0; i <= j; i++)
                {
                    if (aBin[i])
                    {
                        comp.InverseCPhaseShift(Math.Abs(j - i), b[j], controls);
                    }
                }
            }
        }
        public static void AddQFTPhi(this QuantumComputer comp, ulong a, Register b, params RegisterRef[] controls)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, a, b, controls };
                comp.AddParametricGate("AddQFTPhi", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            bool[] aBin = Quantum.Utils.getBinaryRepresentation(a, b.Width);

            for (int j = b.Width - 1; j >= 0; j--)
            {
                //comp.ClassicalCPhaseShift(b[j], aBin[j], b.Width - j, controls);
                for (int i = j; i >= 0; i--)
                {
                    if (aBin[i])
                    {
                        comp.CPhaseShift(Math.Abs(j - i), b[j], controls);
                    }
                }
            }
        }
        public static void Carry(this QuantumComputer comp,
            RegisterRef rc0, RegisterRef ra0, RegisterRef rb0, RegisterRef rc1)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, rc0, ra0, rb0, rc1 };
                comp.AddParametricGate("Carry", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Register root = comp.GetRootRegister(rc0, ra0, rb0, rc1);
            int c0 = rc0.OffsetToRoot;
            int a0 = ra0.OffsetToRoot;
            int b0 = rb0.OffsetToRoot;
            int c1 = rc1.OffsetToRoot;

            root.Toffoli(c1, a0, b0);
            root.CNot(b0, a0);
            root.Toffoli(c1, c0, b0);
        }
        // register X - initially loaded with x
        // register X_1 - initially loaded with 1
        // width(x1) = W + 1
        // width(X) is 2 * W
        // after computation register X_1 changes and contains: [(a^x) mod N]
        // Secure version: throws Exceptions if arguments are invalid
        public static void ExpModulo(
            this QuantumComputer comp,
            Register x,
            Register x1,
            int valueA,
            int valueN)
        {
            if (comp.Group)
            {
                object[] parameters = new object[] { comp, x, x1, valueA, valueN };
                comp.AddParametricGate("ExpModulo", parameters);
                return;
            }
            else
            {
                comp.Group = true;
            }

            Validate(x, x1, valueN);

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

            comp.ExpModulo(a, b, c, N, x1, x, valueA, valueN);

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