private int[] FindBestTrail(int[][] dists, int numCities)
        {
            random = new Random(0);

            var ants = InitAnts(numCities);

            // initialize ants to random trails

            int[] bestTrail = BestTrail(ants, dists);
            // determine the best initial trail
            double bestLength = Length(bestTrail, dists);
            // the length of the best trail

            Pheromone pheromone = new Pheromone(numCities);

            int time = 0;

            while (time < maxTime)
            {
                UpdateAnts(ants, pheromone, dists);
                UpdatePheromones(pheromone, ants, dists);

                int[]  currBestTrail  = BestTrail(ants, dists);
                double currBestLength = Length(currBestTrail, dists);
                if (currBestLength < bestLength)
                {
                    bestLength = currBestLength;
                    bestTrail  = currBestTrail;
                    //Console.WriteLine("New best length of " + bestLength.ToString("F1") + " found at " + time);
                }
                time += 1;
            }
            return(bestTrail);
        }
        private void UpdateAnts(Ant[] ants, Pheromone pheromone, int[][] dists)
        {
            int numCities = pheromone.Size;

            for (int k = 0; k <= ants.Length - 1; k++)
            {
                int   start    = isStartPointFixed ? startPoint : random.Next(0, numCities);
                int[] newTrail = isEndPointFixed ? BuildTrail(start, endPoint, pheromone, dists) : BuildTrail(start, pheromone, dists);
                ants[k].Trail = newTrail;
            }
        }
        private double[] MoveProbs(int cityX, bool[] visited, Pheromone pheromone, int[][] dists)
        {
            // for ant, located at nodeX, with visited[], return the prob of moving to each city
            int numCities = pheromone.Size;

            double[] taueta = new double[numCities];
            // inclues cityX and visited cities
            double sum = 0.0;

            // sum of all tauetas
            // i is the adjacent city
            for (int i = 0; i <= taueta.Length - 1; i++)
            {
                if (i == cityX)
                {
                    taueta[i] = 0.0;
                    // prob of moving to self is 0
                }
                else if (visited[i])
                {
                    taueta[i] = 0.0;
                    // prob of moving to a visited city is 0
                }
                else
                {
                    taueta[i] = Math.Pow(pheromone.Get(cityX, i), alpha) * Math.Pow((1.0 / Distance(cityX, i, dists)), beta);
                    // could be huge when pheromone[][] is big
                    if (taueta[i] < 0.0001)
                    {
                        taueta[i] = 0.0001;
                    }
                    else if (taueta[i] > (double.MaxValue / (numCities * 100)))
                    {
                        taueta[i] = double.MaxValue / (numCities * 100);
                    }
                }
                sum += taueta[i];
            }

            double[] probs = new double[numCities];
            for (int i = 0; i <= probs.Length - 1; i++)
            {
                probs[i] = taueta[i] / sum;
                // big trouble if sum = 0.0
            }
            return(probs);
        }
        private int[] BuildTrail(int start, Pheromone pheromone, int[][] dists)
        {
            int numCities = pheromone.Size;

            int[]  trail   = new int[numCities];
            bool[] visited = new bool[numCities];
            trail[0]       = start;
            visited[start] = true;
            for (int i = 0; i <= numCities - 2; i++)
            {
                int cityX = trail[i];
                int next  = NextCity(cityX, visited, pheromone, dists);
                trail[i + 1]  = next;
                visited[next] = true;
            }
            return(trail);
        }
        private void UpdatePheromones(Pheromone pheromone, Ant[] ants, int[][] dists)
        {
            for (int i = 0; i <= pheromone.Size - 1; i++)
            {
                for (int j = i + 1; j <= pheromone.Size - 1; j++)
                {
                    for (int k = 0; k <= ants.Length - 1; k++)
                    {
                        double length = Length(ants[k].Trail, dists);
                        // length of ant k trail
                        double decrease = (1.0 - rho) * pheromone.Get(i, j);
                        double increase = 0.0;
                        if (EdgeInTrail(i, j, ants[k].Trail))
                        {
                            increase = (Q / length);
                        }

                        pheromone.Set(i, j, decrease + increase);
                    }
                }
            }
        }
        private int NextCity(int cityX, bool[] visited, Pheromone pheromone, int[][] dists)
        {
            // for ant (with visited[]), at nodeX, what is next node in trail?
            double[] probs = MoveProbs(cityX, visited, pheromone, dists);

            double[] cumul = new double[probs.Length + 1];
            for (int i = 0; i <= probs.Length - 1; i++)
            {
                cumul[i + 1] = cumul[i] + probs[i];
                // consider setting cumul[cuml.Length-1] to 1.00
            }

            double p = random.NextDouble();

            for (int i = 0; i <= cumul.Length - 2; i++)
            {
                if (p >= cumul[i] && p < cumul[i + 1])
                {
                    return(i);
                }
            }
            throw new Exception("Failure to return valid city in NextCity");
        }