/**
         * This method returns the TSPState of the element with the minimum value and removes it from the queue.
         * Time Complexity: O(log(|V|)) because removing a node is constant time as we have its position in
         * the queue, then to read just the heap we just bubble up the min value which takes as long as
         * the depth of the tree which is log(|V|), where |V| is the number of nodes
         * Space Complexity: O(1) because we don't create any extra variables that vary with the size of the input.
         */
        public TSPState deleteMin()
        {
            // grab the node with min value which will be at the root
            TSPState minValue = states[1];

            states[1] = states[count];
            count--;
            // fix the heap
            int indexIterator = 1;

            while (indexIterator <= count)
            {
                // grab left child
                int smallerElementIndex = 2 * indexIterator;

                // if child does not exist, break
                if (smallerElementIndex > count)
                {
                    break;
                }

                // if right child exists and is of smaller value, pick it
                if (smallerElementIndex + 1 <= count && states[smallerElementIndex + 1].getPriority() < states[smallerElementIndex].getPriority())
                {
                    smallerElementIndex++;
                }

                if (states[indexIterator].getPriority() > states[smallerElementIndex].getPriority())
                {
                    // set the node's value to that of its smaller child
                    TSPState temp = states[smallerElementIndex];
                    states[smallerElementIndex] = states[indexIterator];
                    states[indexIterator]       = temp;
                }

                indexIterator = smallerElementIndex;
            }
            // return the min value
            return(minValue);
        }
        /**
         * This method updates the nodes in the queue after inserting a new node
         * Time Complexity: O(log(|V|)) as reording the heap works by bubbling up the min value to the top
         * which takes as long as the depth of the tree which is log|V|.
         * Space Complexity: O(1) as it does not create any extra variables that vary with the size of the input.
         */
        public void insert(TSPState newState)
        {
            // update the count
            count++;
            states[count] = newState;
            if (count > maxNum)
            {
                maxNum = count;
            }

            // as long as its parent has a larger value and have not hit the root
            int indexIterator = count;

            while (indexIterator > 1 && states[indexIterator / 2].getPriority() > states[indexIterator].getPriority())
            {
                // swap the two nodes
                TSPState temp = states[indexIterator / 2];
                states[indexIterator / 2] = states[indexIterator];
                states[indexIterator]     = temp;

                indexIterator /= 2;
            }
        }
        //**********************************************BBAlgorithm*********************************************************************************
        /// <summary>
        /// performs a Branch and Bound search of the state space of partial tours
        /// stops when time limit expires and uses BSSF as solution
        /// </summary>
        /// <returns>results array for GUI that contains three ints: cost of solution, time spent to find solution, number of solutions found during search (not counting initial BSSF estimate)</returns>
        public string[] bBSolveProblem()
        {
            string[] results = new string[3];

            // TODO: Add your implementation for a branch and bound solver here.
            //Initalize variables. Takes O(1) space and time
            int numOfCitiesLeft        = Cities.Length;
            int numOfSolutions         = 0;
            int numOfStatesCreated     = 0;
            int numOfStatesNotExpanded = 0;

            //Initalize time variable for stopping the algorithm after the default of 60 seconds. Takes O(1) space and time
            DateTime start = DateTime.Now;
            DateTime end   = start.AddSeconds(time_limit / 1000);

            //Create initial root state and set its priority to its lower bound. Takes O(n^2) space and time as discussed above
            TSPState initialState = createInitialState();

            numOfStatesCreated++;
            initialState.setPriority(calculateKey(numOfCitiesLeft - 1, initialState.getLowerBound()));

            //Create initial BSSF greedily
            double bssfBound = createGreedyBssf();

            PriorityQueue queue = new PriorityQueue(Cities.Length);

            queue.insert(initialState);

            // Branch and Bound until the queue is empty, we have exceeded the time limit, or we found the optimal solution

            /* This loop will have a iterate 2^n times approximately with expanding and pruning for each state, then for each state it
             * does O(n^2) work by reducing the matrix, so over all O((n^2)*(2^n)) time and space as well as it creates a nxn
             * matrix for each state*/
            while (!queue.isEmpty() && DateTime.Now < end && queue.getMinLB() != bssfBound)
            {
                // Grab the next state in the queue
                TSPState currState = queue.deleteMin();

                // check if lower bound is less than the BSSF, else prune it
                if (currState.getLowerBound() < bssfBound)
                {
                    // Branch and create the child states
                    for (int i = 0; i < Cities.Length; i++)
                    {
                        // First check that we haven't exceeded the time limit
                        if (DateTime.Now >= end)
                        {
                            break;
                        }

                        // Make sure we are only checking cities that we haven't checked already
                        if (currState.getPath().Contains(Cities[i]))
                        {
                            continue;
                        }

                        // Create the State
                        double[,] oldCostMatrix = currState.getCostMatrix();
                        double[,] newCostMatrix = new double[Cities.Length, Cities.Length];
                        // Copy the old array in the new one to modify the new without affecting the old
                        for (int k = 0; k < Cities.Length; k++)
                        {
                            for (int l = 0; l < Cities.Length; l++)
                            {
                                newCostMatrix[k, l] = oldCostMatrix[k, l];
                            }
                        }
                        City   lastCityinCurrState = (City)currState.getPath()[currState.getPath().Count - 1];
                        double oldLB = currState.getLowerBound();
                        setUpMatrix(ref newCostMatrix, Array.IndexOf(Cities, lastCityinCurrState), i, ref oldLB);
                        double    newLB   = oldLB + reduceMatrix(ref newCostMatrix);
                        ArrayList oldPath = currState.getPath();
                        ArrayList newPath = new ArrayList();
                        foreach (City c in oldPath)
                        {
                            newPath.Add(c);
                        }
                        newPath.Add(Cities[i]);
                        TSPState childState = new TSPState(ref newPath, ref newLB, ref newCostMatrix);
                        numOfStatesCreated++;

                        // Prune States larger than the BSSF
                        if (childState.getLowerBound() < bssfBound)
                        {
                            City   firstCity      = (City)childState.getPath()[0];
                            City   lastCity       = (City)childState.getPath()[childState.getPath().Count - 1];
                            double costToLoopBack = lastCity.costToGetTo(firstCity);

                            // If we found a solution and it goes back from last city to first city
                            if (childState.getPath().Count == Cities.Length && costToLoopBack != double.MaxValue)
                            {
                                childState.setLowerBound(childState.getLowerBound() + costToLoopBack);
                                bssf      = new TSPSolution(childState.getPath());
                                bssfBound = bssf.costOfRoute();
                                numOfSolutions++;
                                numOfStatesNotExpanded++; // this state is not expanded because it is not put on the queue
                            }
                            else
                            {
                                // Set the priority for the state and add the new state to the queue
                                numOfCitiesLeft = Cities.Length - childState.getPath().Count;
                                childState.setPriority(calculateKey(numOfCitiesLeft, childState.getLowerBound()));
                                queue.insert(childState);
                            }
                        }
                        else
                        {
                            numOfStatesNotExpanded++; // States that are pruned are not expanded
                        }
                    }
                }
                currState = null;
            }
            numOfStatesNotExpanded += queue.getSize(); // if the code terminated before queue is empty, then those states never got expanded
            Console.WriteLine("Number of states generated: " + numOfStatesCreated);
            Console.WriteLine("Number of states not Expanded: " + numOfStatesNotExpanded);
            Console.WriteLine("Max Number of states put in queue: " + queue.getMaxNumOfItems());
            end = DateTime.Now;
            TimeSpan diff    = end - start;
            double   seconds = diff.TotalSeconds;

            results[COST]  = System.Convert.ToString(bssf.costOfRoute());   // load results into array here, replacing these dummy values
            results[TIME]  = System.Convert.ToString(seconds);
            results[COUNT] = System.Convert.ToString(numOfSolutions);

            return(results);
        }