/* Establishes connections for n_row by n_col grid with
   max Euclidean distance r_c, and c connections
   per cell placed randomly. conn is an (n_row*n_col,4)
   array. Row i gives the numbers of the cells cell i
   is connected to
*/
#include <math.h>
#include <stdlib.h>
#include <limits.h>
#include "connections.h"

static int within_bounds(int*,int*,int,int,double);
static int already_connected(int conn[][4],int,int);

void connections(int conn[][4],int n_row,int n_col,double r_c,double c){
  
  int x,y,i,a[2],b[2],a_index,b_index,rel_coord;
  unsigned long m, max_conn;
  int M=n_row*n_col;
  int n_conn[M];
  int d = floor(r_c);
  int rc_list[(int)pow(2*d+1,2)][2];

  // initialize the conn and n_conn matrices
  for(x=0;x<M;x++){
    n_conn[x] = 0;
    for(i=0;i<4;i++){
      conn[x][i] = -1;
    }
  }
  
  srand(2);
  
  // number of connections to make
  if(M>1){
    max_conn = factorial(M-1);
  }else{
    max_conn = 0;
  }
  if(ceil(M*c/2) < max_conn){
    m = ceil(M*c/2);
  }else{
    m = max_conn;
  }
  
  // relative indexes of cells r_c from (0,0)
  i = 0;
  for(x=-d;x<=d;x++){
    for(y=-d;y<=d;y++){
      rc_list[i][0] = x;
      rc_list[i][1] = y;
      i = i+1;
    }
  }

  i = 0;
  while(m>0){
    // choose random cell a
    a_index = floor((double)rand()*M/RAND_MAX);
    index_to_coord(a_index,n_row,n_col,a);
    
    // choose random cell b, at most r_c away from a,
    // include phantom cells
    rel_coord = floor(rand()*pow(2*d+1,2)/RAND_MAX);
    b[0] = rc_list[rel_coord][0] + a[0];
    b[1] = rc_list[rel_coord][1] + a[1];
    b_index = coord_to_index(b[0],b[1],n_row,n_col);
    
    if(within_bounds(a,b,n_row,n_col,r_c) & 
       n_conn[a_index] < 4 & n_conn[b_index] < 4 & 
       !already_connected(conn,a_index,b_index)){
      // cell is not phantom, connection not already made
      // set connection
      conn[a_index][n_conn[a_index]] = b_index;
      n_conn[a_index] = n_conn[a_index] + 1;
      conn[b_index][n_conn[b_index]] = a_index;
      n_conn[b_index] = n_conn[b_index] + 1;;
      m = m-1;
    }
    i = i+1;
  }
  return;
}

int coord_to_index(int x,int y,int n_row,int n_col){
  // translates row x, col y to cell #,
  // ordered by row then column
  // first row/col is #0, first cell is #0
  
  int index = x*n_col + y;
  return index;
}

int* index_to_coord(int index,int n_row,int n_col,int* coord){
  // give the coordinates in row and column # when
  // given the cells #
  // first row/col is #0, first cell is #0
  
  // row coordinate
  coord[0] = index/n_col;

  // column coordinate
  coord[1] = fmod(index,n_col);
  return coord;
}

int within_bounds(int* a,int* b,int n_row,int n_col,double r_c){
  /* Check if b is on the grid and in range of a */
  int yes;
  yes = 0<=b[0] & b[0]<n_row;
  yes = yes & 0<=b[1] & b[1]<n_col;
  yes = yes & sqrt(pow(a[0]-b[0],2)+pow(a[1]-b[1],2))<=r_c;
  return yes;
}

int already_connected(int conn[][4], int a_index,int b_index){
  int yes = 0;
  int k;
  if(a_index == b_index){
    return 1;
  }
  for(k=0;k<4;k++){
    if(conn[a_index][k] == b_index){
      yes = 1;
    }
  }
  return yes;
}