// L-38 MCS 360 Mon 27 Nov 2017 : adjacency_matrix.cpp

/* The adjacency matrix representation of a graph is a Boolean matrix.
   If two vertices are connected, then the (i,j)-th entry in the matrix
   is true, otherwise the (i,j)-th entry is false.
   A graph can be directed or undirected, weighted or not.  */

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <vector>

using namespace std;

void allocate_boolean_matrix ( vector< vector<bool> >& mat, size_t dim );
// Allocates space for an adjacency matrix of dimension dim.

void allocate_double_matrix ( vector< vector<double> >& mat, size_t dim );
// Allocates space for an adjacency matrix of dimension dim.

void random_directed_graph ( vector< vector<bool> >& mat );
// Fills up the adjacency matrix for a random directed graph.

void random_directed_weighted_graph ( vector< vector<double> >& mat );
// Fills up the adjacency matrix for a random directed graph.
// The weights are random integers between 0.1 and 9.9.

void random_undirected_graph ( vector< vector<bool> >& mat );
// Fills up the adjacency matrix for a random undirected graph.
// The matrix will be a symmetric matrix.

void random_undirected_weighted_graph ( vector< vector<double> >& mat );
// Fills up the adjacency matrix for a random undirected graph.
// The matrix will be a symmetric matrix.
// The weights are random integers between 0.1 and 9.9.

void write_boolean_matrix ( vector< vector<bool> >& mat );
// Writes the matrix to screen.

void write_double_matrix ( vector< vector<double> >& mat );
// Writes the matrix to screen.

int main ( void )
{
   cout << "Give the number of vertices : ";
   size_t n; cin >> n;

   cout << "Directed graph ? (y/n) ";
   char directed; cin >> directed;

   cout << "Weighted graph ? (y/n) ";
   char weighted; cin >> weighted;

   if(weighted == 'y')
   {
      vector< vector<double> > mat;

      allocate_double_matrix(mat, n);

      if(directed == 'y')
         random_directed_weighted_graph(mat);
      else
         random_undirected_weighted_graph(mat);

      write_double_matrix(mat);
   }
   else
   {
      vector< vector<bool> > mat;

      allocate_boolean_matrix(mat, n);

      if(directed == 'y')
         random_directed_graph(mat);
      else
         random_undirected_graph(mat);

      write_boolean_matrix(mat);
   }
   return 0;
}

void allocate_boolean_matrix ( vector< vector<bool> >& mat, size_t dim )
{
   for(size_t i=0; i<dim; i++)
   {
      vector<bool> row;

      for(size_t j=0; j<dim; j++) row.push_back(0);

      mat.push_back(row);
   }
}

void allocate_double_matrix ( vector< vector<double> >& mat, size_t dim )
{
   for(size_t i=0; i<dim; i++)
   {
      vector<double> row;

      for(size_t j=0; j<dim; j++) row.push_back(0.0);

      mat.push_back(row);
   }
}

void random_directed_graph ( vector< vector<bool> >& mat )
{
   const size_t dim = mat.size();

   for(size_t i=0; i<dim; i++)
      for(size_t j=0; j<dim; j++)     // place edges at random
         mat[i][j] = (rand() % 2); // generate random bits
}

void random_directed_weighted_graph ( vector< vector<double> >& mat )
{
   const size_t dim = mat.size();

   for(size_t i=0; i<dim; i++)
      for(size_t j=0; j<dim; j++)  // place edges at random
      {
         size_t rnd = rand() % 2;  // edge or not
         if(rnd == 0)
            mat[i][j] = 0.0;       // no edge => zero weight
         else
         {
            rnd = 1 + rand() % 99; // random weight 
            mat[i][j] = ((double) rnd)/10.0; // in [0.1, 9.9]
         }
      }
}

void random_undirected_graph ( vector< vector<bool> >& mat )
{
   const size_t dim = mat.size();

   for(size_t i=0; i<dim; i++)
   {
      for(size_t j=0; j<i; j++)    // adjacency matrix is symmetric
         mat[i][j] = mat[j][i];    // lower half is the upper half
      for(size_t j=i; j<dim; j++)  // place edges at random
         mat[i][j] = (rand() % 2); // generate random bits
   }  
}

void random_undirected_weighted_graph ( vector< vector<double> >& mat )
{
   const size_t dim = mat.size();

   for(size_t i=0; i<dim; i++)
   {
      for(size_t j=0; j<i; j++)    // adjacency matrix is symmetric
         mat[i][j] = mat[j][i];    // lower half is the upper half

      for(size_t j=i; j<dim; j++)  // place edges at random
      {
         size_t rnd = rand() % 2;
         if(rnd == 0)
            mat[i][j] = 0.0;
         else
         {
            rnd = 1 + rand() % 99; // random weight 
            mat[i][j] = ((double) rnd)/10.0; // in [0.1, 9.9]
         }
      }
   }  
}

void write_boolean_matrix ( vector< vector<bool> >& mat )
{
   const size_t dim = mat.size();
   
   for(size_t i=0; i<dim; i++)
   {
      for(size_t j=0; j<dim; j++)
         cout << " " << mat[i][j];
      cout << endl;
   }  
}

void write_double_matrix ( vector< vector<double> >& mat )
{
   const size_t dim = mat.size();

   cout << fixed << setprecision(1);
   
   for(size_t i=0; i<dim; i++)
   {
      for(size_t j=0; j<dim; j++)
         cout << " " << mat[i][j];
      cout << endl;
   }  
}
