/* SI 335 Spring 2012
 * Project 6
 * This is a program to generate test cases for the "drive"
 * shortest-paths finder program. It takes in n and m and
 * writes a randomly-generated graph to the named output file.
 */

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

using namespace std;

// Returns a random integer between 0 and n-1.
int randomInt(int n);

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

int main (int argc, char** argv) {
  srand(time(NULL));

  // Check command-line arguments
  if (argc != 4) {
    cerr << "Usage: " << argv[0] << " <n> <m> <output_file>" << endl;
    error ("Wrong number of arguments");
  }

  int n, m;
  n = atoi(argv[1]);
  m = atoi(argv[2]);

  if (n <= 0 || m <= 0) error ("n and m must be positive");
  if (n > 10000) error ("n is too large");
  if (m > n*(n-1)/2) error ("m is too large");

  ofstream graph (argv[3]);
  if (!graph) error("Bad output filename");

  // Read in city names, then put into random order.
  ifstream citynames ("cities.txt");
  if (!citynames) error("Could not find file cities.txt");

  vector<string> cities(n);
  for (int i=0; i<n; ++i)
    citynames >> cities[i];

  citynames.close();

  random_shuffle(cities.begin(), cities.end());

  // Print n, m to graph file
  graph << n << ' ' << m << endl;

  // Print city names to graph file
  for (int i=0; i<n; ++i)
    graph << cities[i] << endl;

  // Make a matrix to store whether we've already put
  // an edge between two vertices.
  bool** edges = new bool*[n];
  edges[0] = new bool[n*n];
  for (int i=1; i<n; ++i)
    edges[i] = edges[i-1]+n;
  
  if (m <= n*(n-1)/4) {
    // Graph is (mostly) sparse. So start with empty graph and
    // generate random edges.
    for (int i=0; i<n; ++i) {
      for (int j=0; j<n; ++j)
        edges[i][j] = false;
      edges[i][i] = true;
    }

    int i=0;
    // Start by generating a random tree, so the graph will be connected.
    for (; i<n-1 && i<m; ++i) {
      int u = i+1;
      int v;
      do { v = randomInt(u); } while (edges[u][v]);
      graph << cities[u] << ' ' << cities[v] << ' ';
      graph << randomInt(400) << ' ' << randomInt(400) << endl;
      edges[u][v] = edges[v][u] = true;
    }

    // Now just generate random edges
    for (; i<m; ++i) {
      int u,v;
      do {
        u = randomInt(n);
        v = randomInt(n);
      } while (edges[u][v]);
      graph << cities[u] << ' ' << cities[v] << ' ';
      graph << randomInt(400) << ' ' << randomInt(400) << endl;
      edges[u][v] = edges[v][u] = true;
    }
  }
  else {
    // Graph is (mostly) dense. So start with a complete graph and randomly
    // remove edges.
    for (int i=0; i<n; ++i) {
      for (int j=0; j<n; ++j)
        edges[i][j] = true;
      edges[i][i] = false;
    }

    for (int i=m; i<n*(n-1)/2; ++i) {
      int u,v;
      do {
        u = randomInt(n);
        v = randomInt(n);
      } while (!edges[u][v]);
      edges[u][v] = edges[v][u] = false;
    }

    for (int i=0; i<n; ++i) {
      for (int j=i+1; j<n; ++j) {
        if (edges[i][j]) {
          graph << cities[i] << ' ' << cities[j] << ' ';
          graph << randomInt(400) << ' ' << randomInt(400) << endl;
        }
      }
    }
  }

  graph.close();
  delete [] edges[0];
  delete [] edges;

  return 0;
}

// Returns a random integer between 0 and n-1.
int randomInt(int n) {
  int r, max = RAND_MAX - ((RAND_MAX-n+1) % n);
  do { r = rand(); }
  while (r > max);
  return (r % n);
}

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