/// <summary>
        /// Unload a benchmark from memory.
        /// </summary>
        private static void UnloadBenchmark()
        {
            ConsoleLayout.Header("Select a benchmark");

            if (Benchmarks.LoadedBenchmarks.Count == 0)
            {
                Console.WriteLine("There are no benchmarks loaded...");
            }
            else
            {
                for (int i = 0; i < Benchmarks.LoadedBenchmarks.Count; i++)
                {
                    Console.WriteLine(i + 1 + ": " + Benchmarks.LoadedBenchmarks[i].Name);
                }

                try
                {
                    Console.Write("\nGive the ID of the benchmark to unload: ");
                    int index = Convert.ToInt32(Console.ReadLine());
                    Console.WriteLine("\nThe bencmark '{0}' has been unloaded.", Benchmarks.LoadedBenchmarks[index - 1].Name);
                    Benchmarks.LoadedBenchmarks.RemoveAt(index - 1);
                }
                catch (Exception e) when(e is ArgumentOutOfRangeException || e is FormatException)
                {
                    ConsoleLayout.Error("Failed to unload a benchmark");
                }
            }
            ConsoleLayout.Footer();
        }
 static void Main(string[] args)
 {
     while (true)
     {
         ConsoleLayout.Header("Main");
         int selection = ConsoleLayout.SelectFromMenu(MainMenu);
         try
         {
             if (selection == 0)
             {
                 return;
             }
             else if (selection == 42)
             {
                 Test();
             }
             else
             {
                 MainMenu[selection - 1].Item2.Invoke();
             }
         } catch (InvalidCastException)
         {
             ConsoleLayout.Error();
             ConsoleLayout.Footer();
         }
     }
 }
        /// <summary>
        /// Load a benchmark to memory.
        /// </summary>
        private static void LoadBenchmark()
        {
            ConsoleLayout.Header("Select a benchmark");

            List <FileInfo> files = (new DirectoryInfo(Benchmarks.BenchmarkFolder)).GetFiles().ToList();

            for (int i = 0; i < files.Count; i++)
            {
                Console.WriteLine(i + 1 + ": " + files[i].Name);
            }

            try
            {
                Console.Write("\nGive the ID of the benchmark to load: ");
                int index = Convert.ToInt32(Console.ReadLine());
                if (Benchmarks.LoadedBenchmarks.Contains(files[index - 1]))
                {
                    Console.WriteLine("This benchmark is already loaded.");
                }
                else
                {
                    Console.WriteLine("\nThe benchmark '{0}'  has been loaded.", files[index - 1].Name);
                    Benchmarks.LoadedBenchmarks.Add(files[index - 1]);
                }
            }
            catch (Exception e) when(e is ArgumentOutOfRangeException || e is FormatException)
            {
                ConsoleLayout.Error("Failed to load a benchmark");
            }

            ConsoleLayout.Footer();
        }
        /// <summary>
        /// Add a new dependency rule.
        /// </summary>
        private static void AddNewRandomBenchmark()
        {
            ConsoleLayout.Header("Add a new dependency rule");

            try
            {
                Console.Write("The number of gateparts that are involved: ");
                int nbGateParts = Convert.ToInt32(Console.ReadLine());
                Console.WriteLine();
                List <GatePart> gateParts = new List <GatePart>();
                for (int i = 0; i < nbGateParts; i++)
                {
                    Console.Write("Gatepart " + (i + 1) + ": ");
                    string gatePartName = Console.ReadLine();
                    gateParts.Add((GatePart)Enum.Parse(typeof(GatePart), gatePartName));
                    if (gatePartName == "None")
                    {
                        throw new ArgumentException();
                    }
                }
                DependencyRule rule = new DependencyRule(gateParts);
                AlgorithmParameters.AvailableDependencyRules.Add(rule);
                Console.WriteLine("\nThe dependency rule '{0}' has been added.", rule);
            } catch (Exception e) when(e is ArgumentOutOfRangeException || e is FormatException)
            {
                ConsoleLayout.Error("Failed to add a new dependency rule");
            } catch (ArgumentException)
            {
                ConsoleLayout.Error("This is no valid gatepart, the addition of a dependency rule is aborted.");
            }

            ConsoleLayout.Footer();
        }
        /// <summary>
        /// Unload a dependency rule.
        /// </summary>
        private static void UnloadDependencyRule()
        {
            ConsoleLayout.Header("Select a dependency rule");

            if (AlgorithmParameters.DependencyRules.Count == 0)
            {
                Console.WriteLine("There are no dependency rules loaded...");
            }
            else
            {
                for (int i = 0; i < AlgorithmParameters.DependencyRules.Count; i++)
                {
                    Console.WriteLine(i + 1 + ": " + AlgorithmParameters.DependencyRules[i]);
                }

                try
                {
                    Console.Write("\nGive the ID of the dependency rule to unload: ");
                    int index = Convert.ToInt32(Console.ReadLine());
                    Console.WriteLine("\nThe '{0}'-rule has been unloaded.", AlgorithmParameters.DependencyRules[index - 1]);
                    AlgorithmParameters.DependencyRules.RemoveAt(index - 1);
                }
                catch (Exception e) when(e is ArgumentOutOfRangeException || e is FormatException)
                {
                    ConsoleLayout.Error("Failed to unload a dependency rule");
                }
            }

            ConsoleLayout.Footer();
        }
        /*****************************************************************
        * TRANSFORMATION ALGORITHMS
        *****************************************************************/

        /// <summary>
        /// Load a transformation algorithm.
        /// </summary>
        private static void LoadTransformationAlgorithm()
        {
            ConsoleLayout.Header("Select a transformation algorithm");

            if (AlgorithmParameters.AvailableTransformationAlgorithms.Count == 0)
            {
                Console.WriteLine("There are no available transformation algorithms...");
            }
            else
            {
                var orderedTransformation = AlgorithmParameters.AvailableTransformationAlgorithms.OrderBy(item => item.GetType().FullName);
                for (int i = 0; i < orderedTransformation.Count(); i++)
                {
                    Console.WriteLine(i + 1 + ": " + orderedTransformation.ElementAt(i).Name());
                    Console.WriteLine(orderedTransformation.ElementAt(i).Parameters() + '\n');
                }

                try
                {
                    Console.Write("Give the ID of the transformation algorithm to load: ");
                    int index = Convert.ToInt32(Console.ReadLine());
                    AlgorithmParameters.Transformation = orderedTransformation.ElementAt(index - 1);
                    Console.WriteLine("\nTransformation algorithm {0} has been loaded.", index);
                }
                catch (Exception e) when(e is ArgumentOutOfRangeException || e is FormatException)
                {
                    ConsoleLayout.Error("Failed to load a transformation algorithm");
                }
            }

            ConsoleLayout.Footer();
        }
        /*****************************************************************
        * TESTING
        *****************************************************************/

        /// <summary>
        /// Test all available initial mapping algorithms.
        /// </summary>
        private static void TestAvailableInitialMappings()
        {
            int nbRep = -1;

            while (nbRep <= 0)
            {
                try
                {
                    ConsoleLayout.Header("Initial mapping test");
                    Console.Write("The number of times to repeat each algorithm: ");
                    nbRep = Convert.ToInt32(Console.ReadLine());
                    if (nbRep <= 0)
                    {
                        throw new FormatException();
                    }
                } catch (FormatException)
                {
                    ConsoleLayout.Error();
                    Console.WriteLine("PRESS ENTER TO CONTINUE ...");
                    Console.ReadLine();
                }
            }


            string path = @"C:\Users\User\Documents\GitHub\QuantumCircuitTransformation\QuantumCircuitTransformation\Results";

            Console.Write("The name for the excel file: ");
            string       fileName     = Console.ReadLine();
            Architecture architecture = QuantumDevices.IBM_Q20;

            using (ExcelPackage e = new ExcelPackage())
            {
                Mapping         mapping;
                LogicalCircuit  lCircuit;
                PhysicalCircuit pCircuit;
                ExcelWorksheet  ws;
                double          timeInitialMapping, timeTransforamtion;
                int             nbGatesPhysical;

                foreach (InitialMapping im in AlgorithmParameters.AvailableInitialMappings)
                {
                    e.Workbook.Worksheets.Add(im.GetFullShort());
                    ws = e.Workbook.Worksheets[im.GetFullShort()];
                    ws.Cells["A1"].Value = "Benchmark";
                    ws.Cells["B1"].Value = "Initial nb gates";
                    ws.Cells["C1"].Value = "Initial mapping time";
                    ws.Cells["D1"].Value = "Total Transformation time";
                    ws.Cells["E1"].Value = "Extra nb gates";


                    for (int i = 0; i < Benchmarks.LoadedBenchmarks.Count; i++)
                    {
                        lCircuit = CircuitGenerator.ReadFromFile(Benchmarks.LoadedBenchmarks[i].Name);
                        ws.Cells["A" + (i + 2)].Value = Benchmarks.LoadedBenchmarks[i].Name;
                        ws.Cells["B" + (i + 2)].Value = lCircuit.NbGates;

                        Console.WriteLine(Benchmarks.LoadedBenchmarks[i].Name);

                        timeInitialMapping = 0;
                        timeTransforamtion = 0;
                        nbGatesPhysical    = 0;

                        foreach (Transformation tr in AlgorithmParameters.AvailableTransformationAlgorithms)
                        {
                            for (int j = 0; j < nbRep; j++)
                            {
                                Globals.Timer.Restart();
                                (mapping, _) = im.Execute(architecture, lCircuit);
                                Globals.Timer.Stop();
                                timeInitialMapping += Globals.Timer.Elapsed.TotalMilliseconds;

                                Globals.Timer.Restart();
                                pCircuit = tr.Execute(lCircuit, architecture, mapping);
                                Globals.Timer.Stop();
                                timeTransforamtion += Globals.Timer.Elapsed.TotalMilliseconds;
                                nbGatesPhysical    += pCircuit.NbGates;
                            }
                        }
                        ws.Cells["C" + (i + 2)].Value = timeInitialMapping / nbRep;
                        ws.Cells["D" + (i + 2)].Value = timeTransforamtion / nbRep;
                        ws.Cells["E" + (i + 2)].Value = nbGatesPhysical / nbRep - lCircuit.NbGates;
                    }
                }

                e.SaveAs(new FileInfo(@path + @"\" + fileName + ".xlsx")); // Save excel
            }

            ConsoleLayout.Footer();
        }
        /// <summary>
        /// Add a new transformation algorithm.
        /// </summary>
        private static void AddTransformationAlgorithm()
        {
            ConsoleLayout.Header("Add a transformation algorithm");

            var TransformationAlgorithms = Assembly
                                           .GetAssembly(typeof(InitialMapping))
                                           .GetTypes()
                                           .Where(t => t.IsSubclassOf(typeof(Transformation)));

            Console.WriteLine("Select a transformation algorithm to add.");
            for (int i = 0; i < TransformationAlgorithms.Count(); i++)
            {
                Console.WriteLine(i + 1 + ": " + TransformationAlgorithms.ElementAt(i).GetTypeInfo().Name);
            }
            Console.Write("Choice: ");

            try
            {
                int  index = Convert.ToInt32(Console.ReadLine());
                Type type  = TransformationAlgorithms.ElementAt(index - 1);
                ConstructorInfo[] constructors = type.GetConstructors().ToList().FindAll(c => c.IsPublic).ToArray();
                ConstructorInfo   constructorInfo;
                if (constructors.Count() == 1)
                {
                    constructorInfo = constructors[0];
                }
                else
                {
                    Console.WriteLine();
                    for (int i = 0; i < constructors.Count(); i++)
                    {
                        Console.WriteLine(i + 1 + ": " + type.Name + " " + constructors[i].ToString().Substring(10));
                    }
                    Console.Write("Choice: ");
                    int index2 = Convert.ToInt32(Console.ReadLine());
                    constructorInfo = constructors[index2 - 1];
                }

                Console.WriteLine();
                object[] param = new object[constructorInfo.GetParameters().Count()];
                for (int i = 0; i < constructorInfo.GetParameters().Count(); i++)
                {
                    Console.Write("> " + constructorInfo.GetParameters()[i].Name + ": ");
                    Type paramType = constructorInfo.GetParameters()[i].ParameterType;
                    param[i] = Convert.ChangeType(Console.ReadLine(), paramType);
                }
                Transformation Transformation = (Transformation)constructorInfo.Invoke(param);
                if (!AlgorithmParameters.AvailableTransformationAlgorithms.Contains(Transformation))
                {
                    AlgorithmParameters.AvailableTransformationAlgorithms.Add(Transformation);
                }

                Console.WriteLine("The transformation algorithm is now available.");
            }
            catch (Exception e) when(e is IndexOutOfRangeException || e is ArgumentOutOfRangeException || e is FormatException)
            {
                Console.WriteLine();
                ConsoleLayout.Error();
            }

            ConsoleLayout.Footer();
        }