import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class TSPUsingMST
{
    // ArraysC to keep track of info. related to each city
    private String[]   cityName;
    private String[]   cityState;
    private int[]      cityLat;
    private int[]      cityLong;
    private int[]      cityPop;
    // 2-D array to keep track of pairwise distances between cities
    private int[][]    distances;
    // number of cities
    private static int numCities;

    public TSPUsingMST(int n)
    {
        numCities = n;
        // Allotting the space for each 1-D array
        cityName = new String[numCities];
        cityState = new String[numCities];
        cityLat = new int[numCities];
        cityLong = new int[numCities];
        cityPop = new int[numCities];
        // Allocate space for each 2-D array. These arrays have 0 elements in
        // row 0,
        // 1 element in row 1, 2 elements in row 2, etc.
        distances = new int[numCities][];
        for (int i = 0; i < numCities; i++)
            distances[i] = new int[i];
        try
            {
                // Construct a buffered reader object and connect it to the files
                // "miles.dat"
                BufferedReader in = new BufferedReader(new FileReader("miles.dat"));
                // A counter that keeps track of the index of the current city being
                // read
                int cityNumber = 0;
                // While-loop for reading in data from "miles.dat." At the beginning
                // of the while-loop
                // the expectation is that we'll be reading a line containing the
                // city name. Instead,
                // if we encounter a line that starts with "*" then we skip to the
                // next line
                while (cityNumber < numCities)
                    {
                        // Read in a line
                        String line = in.readLine();
                        // Skip the rest of the loop if line starts with a "*"
                        if (line.charAt(0) == '*')
                            continue;
                        // Otherwise tokenize the line
                        StringTokenizer tokenizedLine = new StringTokenizer(line, ",[]");
                        // Putting actual data into correct position in the array
                        cityName[cityNumber] = tokenizedLine.nextToken();
                        cityState[cityNumber] = (tokenizedLine.nextToken()).trim(); // trim()
                        // gets
                        // rid
                        // of
                        // leading/trailing
                        // blanks
                        cityLat[cityNumber] = Integer.parseInt(tokenizedLine
                                                               .nextToken());
                        cityLong[cityNumber] = Integer.parseInt(tokenizedLine
                                                                .nextToken());
                        cityPop[cityNumber] = Integer.parseInt(tokenizedLine
                                                               .nextToken());
                        // while loop to put distances in the array; this may need to
                        // read several lines
                        int mileNumber = 0;
                        while (mileNumber < cityNumber)
                            {
                                // Read a mileage line and tokenize it
                                String mileage = in.readLine();
                                StringTokenizer tokenizedMileage = new StringTokenizer(
                                    mileage, " ");
                                // Read all the mileage data in this line into row
                                // cityNumber; increment
                                // mileNumber after each read
                                while (tokenizedMileage.hasMoreTokens())
                                    {
                                        distances[cityNumber][cityNumber - mileNumber - 1] = Integer
                                                .parseInt(tokenizedMileage.nextToken());
                                        mileNumber++;
                                    }
                            } // end of while reading distances
                        cityNumber++;
                    } // end of while reading cities
                in.close();
            } // end of try
        catch (IOException e)
            {
                System.out.println("File not found.");
            }
    } // end of TSPTester() constructor

    // A simple getIndex method to help test the constructor
    int getIndex(String city, String state)
    {
        int location;
        for (location = 0; location < numCities; location++)
            if ((cityName[location].equals(city))
                    && (cityState[location].equals(state)))
                return location;
        return -1;
    }

    // Print information about a city, given a city index
    void printCityInfo(int index)
    {
        System.out
        .println(cityName[index] + " " + cityState[index] + " "
                 + cityLat[index] + " " + cityLong[index] + " "
                 + cityPop[index]);
    }

    // Print distance information between a given pair of cities
    void printDistanceInfo(int i, int j)
    {
        if (i < j)
            System.out.println(distances[j][i]);
        else
            System.out.println(distances[i][j]);
    }

    int getDistance(int i, int j)
    {
        if (i < j)
            return distances[j][i];
        else if (j < i)
            return distances[i][j];
        else
            return 0;
    }

    int[] greedyTSP()
    {
        // Find a cheapest triangle
        // Load triangle 0-1-2 into the the first 3 slots of the greedy array
        int[] greedy = new int[numCities];
        int currentDistance;
        greedy[0] = 0;
        greedy[1] = 1;
        greedy[2] = 2;
        int currentBestDistance = getDistance(0, 1) + getDistance(1, 2)
                                  + getDistance(2, 0);
        for (int i = 0; i < numCities; i++)
            for (int j = 0; j < i; j++)
                for (int k = 0; k < j; k++)
                    if ((currentDistance = getDistance(i, j)
                                           + getDistance(j, k) + getDistance(i, k)) < currentBestDistance)
                        {
                            greedy[0] = i;
                            greedy[1] = j;
                            greedy[2] = k;
                            currentBestDistance = currentDistance;
                        }
        // Try greedily to add a city that yields the smallest increase
        // in the cost of the tour
        int partialTourSize = 3;
        boolean[] visited = new boolean[numCities];
        for (int i = 0; i < numCities; i++)
            visited[i] = false;
        visited[greedy[0]] = true;
        visited[greedy[1]] = true;
        visited[greedy[2]] = true;
        // Main loop: keep repeating until partial tour covers all cities
        while (partialTourSize < numCities)
            {
                int smallestIncrease = Integer.MAX_VALUE;
                int increase = 0;
                int bestInsertionPoint = 0;
                int bestCity = 0;
                // Scan through all cities, stopping at unvisited cities
                for (int i = 0; i < numCities; i++)
                    {
                        if (!visited[i])
                            {
                                // Consider all possible positions of inserting city i into
                                // the tour
                                // and record the smallest increase
                                for (int j = 0; j < partialTourSize; j++)
                                    {
                                        increase = getDistance(greedy[j], i)
                                                   + getDistance(i, greedy[(j + 1) % numCities])
                                                   - getDistance(greedy[j], greedy[(j + 1)
                                                                 % numCities]);
                                        if (increase < smallestIncrease)
                                            {
                                                smallestIncrease = increase;
                                                bestCity = i;
                                                bestInsertionPoint = j;
                                            } // end of if we have found a smaller increase
                                    } // end of for-j
                            } // end of if not visited
                    } // end of for-i
                // Now we are ready to insert the bestCity at the bestInsertionPoint
                for (int j = partialTourSize - 1; j > bestInsertionPoint; j--)
                    greedy[j + 1] = greedy[j];
                greedy[bestInsertionPoint + 1] = bestCity;
                visited[bestCity] = true;
                partialTourSize++;
            } // end-while
        return greedy;
    }

    void copy(int[] source, int[] dest)
    {
        for (int i = 0; i < dest.length; i++)
            dest[i] = source[i];
    }

    void TSP(int[] R, int partialTourSize, boolean[] visited, int[] T)
    {
        // Base case: we have discovered a tour better than T
        if ((partialTourSize == numCities) && (cost(R) < cost(T)))
            {
                System.out.println("Base case. Tour cost is " + cost(R));
                copy(R, T);
                return;
            }
        // Another base case: our partial tour is not worth completing
        if (cost(R, partialTourSize) >= cost(T))
            return;
        // Recursive case: R is not complete and is currently better than T
        // and is therefore worth completing
        for (int i = 0; i < numCities; i++)
            {
                if (!visited[i])
                    {
                        // System.out.println("Appending " + i);
                        visited[i] = true;
                        R[partialTourSize++] = i;
                        TSP(R, partialTourSize, visited, T);
                        partialTourSize--;
                        visited[i] = false;
                        // System.out.println("Deleting " + i);
                    }
            } // end of for-loop
    } // end of TSP

    double cost(int[] tour)
    {
        return cost(tour, tour.length);
    }

    double cost(int[] tour, int tourSize)
    {
        double c = 0;
        for (int i = 0; i < tourSize - 1; i++)
            c = c + getDistance(tour[i], tour[i + 1]);
        c = c + getDistance(tour[tourSize - 1], tour[0]);
        return c;
    }

    // Main method
    public static void main(String[] args)
    {
        int n = 15;
        TSPUsingMST T = new TSPUsingMST(n);
        // Initialize the list of vertices in the tree
        // Initially, no one except vertex 0 is in the tree
        boolean[] visited = new boolean[n];
        for (int i = 0; i < n; i++)
            visited[i] = false;
        visited[0] = true;
        // Initialize the int[] that maintains the tree to default values
        // No vertices have parents set, except vertex 0 whose parent is itself
        int[] tree = new int[n];
        for (int i = 0; i < n; i++)
            tree[i] = -1;
        tree[0] = 0;
        for (int i = 1; i <= n - 1; i++)
            {
                long minWeight = Long.MAX_VALUE;
                int bestVertex = -1;
                int bestParent = -1;
                for (int j = 0; j < n; j++)
                    {
                        for (int k = 0; k < n; k++)
                            {
                                if ((visited[j]) && (!visited[k]))
                                    {
                                        if (T.getDistance(j, k) < minWeight)
                                            {
                                                minWeight = T.getDistance(j, k);
                                                bestVertex = k;
                                                bestParent = j;
                                            } // end if better distance is found
                                    } // end if an edge between a visited and an unvisited is
                                // found
                            } // end for-k
                    } // end for-j
                // Update visited and tree
                visited[bestVertex] = true;
                tree[bestVertex] = bestParent;
            } // end for-i
        // Printing the MST
        for (int i = 1; i < n; i++)
            System.out.println(T.cityName[i] + " " + T.cityState[i] + ", "
                               + T.cityName[tree[i]] + " " + T.cityState[tree[i]]);
        // Compting the MST cost
        long cost = 0;
        for (int i = 0; i < n; i++)
            cost += T.getDistance(i, tree[i]);
        System.out.println("The cost of the minimum spanning tree is " + cost);
    } // end main method
} // end class

/*
Yankton SD, Wisconsin Dells WI
Yakima WA, Williston ND
Worcester MA, Wilmington DE
Wisconsin Dells WI, Youngstown OH
Winston-Salem NC, Winchester VA
Winnipeg MB, Yankton SD
Winchester VA, Wilmington DE
Wilmington NC, Winston-Salem NC
Wilmington DE, Williamsport PA
Williston ND, Winnipeg MB
Williamsport PA, Youngstown OH
Williamson WV, Winston-Salem NC
Wichita Falls TX, Wichita KS
Wichita KS, Yankton SD
The cost of the minimum spanning tree is 5461