#include "bfs_cilk.h"
#include "pbfs_cilk.h"
#include "bfs_seq.h"

using namespace std;

int main(int argc, char *argv[]) {
  // Ensure proper arugments
  if(argc != 3) {
    cout << "ERROR: proper usage is ./bfs file.txt [p][s]" << std::endl;
    return 1;
  }

  cout << "CILK NWORKERS: " << __cilkrts_get_nworkers() << endl;
  
  FILE* inFile;
  inFile = fopen(argv[1], "r");
  int64_t source = 0;
  int64_t destination = 0;
  int64_t edgeCount = 0;
  int64_t n = 0;  // Number of vertices

  // Scan once to count vertices and edges
  while( !feof(inFile) ) {
    fscanf(inFile, "%i %i", &source, &destination);
    if(feof(inFile)) break;
    edgeCount++;
    if(source > n) n = source;            // The number of vertices should 
    if(destination > n) n = destination;  // equal the highest-numbered vertex.
  }
  fclose(inFile);

  // Allocate the matrix based on how many vertices there are
  // And initialize it all to 0's
  cout << "Edge count: "   << edgeCount << endl;
  cout << "Vertex count: " << n << endl;
  //int64_t* mat = (int64_t *) calloc(n*n, sizeof(int64_t));
  //cout << "Debug 0" << endl;
  int64_t matrix[n+1][n+1];
  for(int64_t i=0; i < n+1; i++) {
    for(int64_t j=0; j < n+1; j++) {
      matrix[i][j] = 0;
    }
  }
  
    
  // Scan again to actually create matrix entries, now that we have allocated it
  inFile = fopen(argv[1], "r");
  for(int64_t i=0; i<edgeCount; i++ ) {
    fscanf(inFile, "%i %i", &source, &destination);
    matrix[source][destination] = 1;
  }
  fclose(inFile);


  // neighbors is the adjacency list
  int64_t neighbors[edgeCount];
  // neighborsIndex contains the starting point in the neighbors array for each node
  int64_t neighborsIndex[n+1];
  int64_t nIndex = 0;

  for(int64_t i = 0; i <= n; i++) {
    neighborsIndex[i] = nIndex;
    for(int64_t j = 0; j <= n; j++) {
      if(matrix[i][j] == 1) {
	      neighbors[nIndex] = j;
	      nIndex++;
      }
    }
  }

  int64_t* BFSdepth = new int64_t[n];
  int64_t* BFSparent = new int64_t;
  
  int64_t* PBFSdepth = new int64_t[n];
  int64_t* PBFSparent = new int64_t;

  char* arg = argv[2];
  std::chrono::time_point<std::chrono::system_clock> start, end;
  std::chrono::duration<double> elapsed_seconds;
  srand (time(NULL));
  if(*arg == 'p') {
    cout << "Running parallel BFS" << endl;
    double totaltime = 0;
    for(int i=0; i<100; i++) {
        int64_t root = rand() % n;
        start = std::chrono::system_clock::now();
        PBFS(neighbors, neighborsIndex, PBFSdepth, root, n, edgeCount);
        end = std::chrono::system_clock::now();
        elapsed_seconds = end-start;
        totaltime += elapsed_seconds.count();
    }
    totaltime = totaltime / 100.0;
    cout << "PBFS took average " << totaltime << " s across 100 runs" << endl;
  }
  else {
    cout << "Running sequential BFS" << endl;
    double totaltime = 0;
    for(int i=0; i<100; i++) {
        int64_t root = rand() % n;
        start = std::chrono::system_clock::now();
        SBFS(neighbors, neighborsIndex, BFSdepth, root, n, edgeCount);
        end = std::chrono::system_clock::now();
        elapsed_seconds = end-start;
        totaltime += elapsed_seconds.count();
    }
    totaltime = totaltime / 100.0;
    cout << "Sequential BFS took average " << totaltime << " s across 100 runs" << endl;
  }
  
  return 0;
}
