/* SI 335 Spring 2012
 * Project 6
 * YOUR NAME HERE
 */

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

// Prints an error message and exits ungracefully
void error (const char* msg);

// Looks up the given vertex name in the array of names,
// and returns the index of that vertex in the array.
// Uses the linearSearch algorithm for search in an unsorted array.
int lookup (const string& name, const string* names, int n);

int main(int argc, char** argv) {
  // Get the filename
  if (argc != 2) {
    cerr << "Usage: " << argv[0] << " <graph_file>" << endl;
    error("Need graph filename as argument."); 
  }
  ifstream graph(argv[1]);

  int n, m;

  // Read in the sizes
  graph >> n >> m;

  if (n <= 0) error("n must be positive");
  if (m <= 0) error("m must be positive");
  
  // Declare storage
  // Location names are stored in an array of strings
  // Edges are stored in a pair of adjacency matrices,
  // one for time and one for cost.
  // The index of a vertex in either matrix is defined as the
  // index of that NAME in the array of strings.
  string* names = new string[n];
  int** times = new int*[n]; // Note: double pointer = 2-D array
  int** costs = new int*[n];
  times[0] = new int[n*n];
  costs[0] = new int[n*n];
  for (int i=1; i<n; ++i) {
    times[i] = times[i-1]+n;
    costs[i] = costs[i-1]+n;
  }

  // Initialize the adjacency matrix.
  // IMPORTANT: I am using the weight of -1 to stand for "infinity".
  for (int i=0; i<n; ++i) {
    for (int j=0; j<n; ++j) {
      if (i == j) {
        times[i][j] = 0;
        costs[i][j] = 0;
      }
      else {
        times[i][j] = -1;
        costs[i][j] = -1;
      }
    }
  }

  // Read in the location names
  for (int i=0; i<n; ++i)
    graph >> names[i];

  // Read in the edges
  string uname, vname;
  int uind, vind;
  int time, cost;
  for (int i=0; i<m; ++i) {
    graph >> uname >> vname >> time >> cost;
    // Look up the index of each name
    uind = lookup (uname, names, n);
    vind = lookup (vname, names, n);
    if (uind < 0 || vind < 0) error ("Invalid edge name");
    // Set the weight in each adjacency matrix,
    // in BOTH DIRECTIONS (for undirected array)
    times[uind][vind] = time;
    times[vind][uind] = time;
    costs[uind][vind] = cost;
    costs[vind][uind] = cost;
  }

  graph.close();

  // Now read in and process the requests
  int timeCost;
  while (cin >> uname >> vname >> timeCost) {
    int uind = lookup (uname, names, n);
    int vind = lookup (vname, names, n);
    if (uind < 0 || vind < 0) error("Invalid starting/ending point");

    /*******************************************************************
     * My algorithm for the shortest paths is REALLY BAD.
     * What it does is a depth-first EXPLORATION to examine every
     * possible path from the starting vertex to find the shortest path
     * to the ending vertex. Once the length of the shortest path is
     * known, it does a SECOND exploration to find (again) that actual
     * path. Notice that this is a depth-first "exploration" as opposed
     * to a normal DFS because it is never marking any nodes as "black",
     * only gray basically. This makes it have exponential time. SLOW!
     ******************************************************************/

    vector<int> curPath; // The currently-explored path
    int curLength = 0;   // The length of curPath
    vector<int> fringe;  // vectors make good stacks.
    int shortest = -1;   // length of the shortest path found so far

    fringe.push_back(uind);
    while (!fringe.empty()) {
      int cur = fringe.back();
      // Search for cur in the curPath (check if that vertex is "gray")
      bool white = true;
      for (int j=0; j<curPath.size(); ++j) {
        if (curPath[j] == cur) white = false;
      }
      if (white) {
        // cur is an unexplored vetex ON THIS PATH.

        // Update curLength (stays at 0 if curPath is empty)
        if (!curPath.empty()) {
          int previous = curPath.back();
          curLength += costs[previous][cur] + timeCost*times[previous][cur];
        }

        // Add cur to the path
        curPath.push_back(cur);

        if (cur == vind) {
          // Found a path to the destination!
          if (shortest == -1 || curLength < shortest)
            shortest = curLength;

          continue; // Skip to the next iteration of the while loop.
        }

        // Add all cur's children to the fringe.
        // Skip children that are already in curPath though!
        for (int child=0; child<n; ++child) {
          bool inpath = false;
          for (int j=0; j<curPath.size(); ++j) {
            if (curPath[j] == child) inpath = true;
          }
          if (!inpath && costs[cur][child] >= 0)
            fringe.push_back(child);
        }
      }
      else {
        // cur has just finished being explored, so pop it off.
        fringe.pop_back();
        curPath.pop_back();
        if (!curPath.empty()) {
          int previous = curPath.back();
          curLength -= costs[previous][cur] + timeCost*times[previous][cur];
        }
      }
    }

    if (curLength < 0) error("No path exists");

    // Now we know that curLength is the length of the shortest path.
    // But we still don't have the actual path! So we have to go through the
    // ENTIRE SEARCH a second time until we find it again.
    fringe.push_back(uind);
    while (!fringe.empty()) {
      int cur = fringe.back();
      // Search for cur in the curPath (check if that vertex is "gray")
      bool white = true;
      for (int j=0; j<curPath.size(); ++j) {
        if (curPath[j] == cur) white = false;
      }
      if (white) {
        // cur is an unexplored vetex ON THIS PATH.

        // Update curLength (stays at 0 if curPath is empty)
        if (!curPath.empty()) {
          int previous = curPath.back();
          curLength += costs[previous][cur] + timeCost*times[previous][cur];
        }

        // Add cur to the path
        curPath.push_back(cur);

        if (cur == vind) {
          // Found a path to the destination!
          if (curLength == shortest) {
            // WE FOUND IT!
            break; // Break out of the while loop.
          }

          continue; // Skip to the next iteration of the while loop.
        }

        // Add all cur's children to the fringe.
        // Skip children that are already in curPath though!
        for (int child=0; child<n; ++child) {
          bool inpath = false;
          for (int j=0; j<curPath.size(); ++j) {
            if (curPath[j] == child) inpath = true;
          }
          if (!inpath && costs[cur][child] >= 0)
            fringe.push_back(child);
        }
      }
      else {
        // cur has just finished being explored, so pop it off.
        fringe.pop_back();
        curPath.pop_back();
        if (!curPath.empty()) {
          int previous = curPath.back();
          curLength -= costs[previous][cur] + timeCost*times[previous][cur];
        }
      }
    }

    // Now print out the actual length and path.
    cout << curLength;
    for (int j=0; j<curPath.size(); ++j)
      cout << ' ' << names[curPath[j]];
    cout << endl;
  }

  // Clean up memory
  delete [] names;
  delete [] times[0];
  delete [] times;
  delete [] costs[0];
  delete [] costs;

  return 0;
}

// Prints an error message and exits ungracefully
void error (const char* msg) {
  cerr << "ERROR:" << endl << msg << endl;
  exit(1);
}

// Looks up the given vertex name in the array of names,
// and returns the index of that vertex in the array.
// Uses the linearSearch algorithm for search in an unsorted array.
int lookup (const string& name, const string* names, int n) {
  for (int i=0; i<n; ++i) {
    if (names[i].compare(name) == 0)
      return i;
  }
  return -1;
}
