// KMB 2005 July 15
// graphs using bitmatrices, i.e. arrays of bitvectors

#include "bitvector.c"
#include "graph_bitmatrix.h"

#define _min(i,j) (i<j?i:j) // FIXME minmax in one operation
#define _max(i,j) (i>j?i:j)
#define _graph_q(b,i,j)      bitvector_q    (b[_min((i),(j))],_max((i),(j)))
#define _graph_get(b,i,j)    bitvector_get  (b[_min((i),(j))],_max((i),(j)))
#define _graph_set(b,i,j)    bitvector_set  (b[_min((i),(j))],_max((i),(j)))
#define _graph_del(b,i,j)    bitvector_clear(b[_min((i),(j))],_max((i),(j)))
#define _graph_flip(b,i,j)   bitvector_flip (b[_min((i),(j))],_max((i),(j)))
#define _graph_degree(g,n,i) bitvector_count(g[i],(n)) // FIXME wrong!!

_graph_t _graph_new(unsigned int nrows) {
  unsigned int i;
  _graph_t b=malloc(nrows*sizeof(bitvector_t));
  for (i=0; i<nrows; i++) b[i]=bitvector_new(nrows);
  return b;
}

void _graph_free(_graph_t b, int nrows) {
  unsigned int i;
  for (i=0; i<nrows; i++) bitvector_free(b[i]);
  free(b);
}

unsigned int _graph_count_edges(_graph_t g, unsigned int nnodes) {
  unsigned int i,c=0;
  for (i=0; i<nnodes; i++) c+=_graph_degree(g,nnodes,i);
  return c;
}

unsigned int _graph_deg_k_count(_graph_t g, unsigned int nnodes, unsigned int k) {
  // number of nodes of degree k
  unsigned int i,c=0;
  if (k>nnodes-1) return 0;
  for (i=0; i<nnodes; i++) if (k==_graph_degree(g,nnodes,i)) c++;
  return c;
}

void _graph_show(_graph_t g, unsigned int n) {
  unsigned int i,j;
  int *row;
  for (i=0; i<n; i++) {
    row=g[i];
    for (j=0; j<n; j++) {
      if (i<j) {
        if (bitvector_q(row,j)) printf("1"); else printf("0");
      } else {
        printf(" ");
      }
    }
    printf("\n");
  }
}

void _graph_write_dotfile(char* fn, _graph_t g, unsigned int n) {
  int i,j;
  FILE *f=fopen(fn,"w");
  fprintf(f,"graph foo {\n");
  fprintf(f,"  /* automatically generated by g_write_dotfile */\n");
  fprintf(f,"  graph [fontcolor=green,fontsize=10,center=true,ratio=1,size=8,8];\n  node  [shape=\"circle\",fixedsize=\"true\"] \n  node  [color=green,fontcolor=red,fontsize=8,height=0.2] \n  edge  [color=blue];\n");
  for (i=0; i<n; i++) for (j=i+1; j<n; j++)
    if (_graph_get(g,i,j)) fprintf(f,"  %d--%d;\n",i,j);
  fprintf(f,"} /* end graph */\n");
  fclose(f);
  fprintf(stderr,"Now do: cat %s | neato -Tps | gv -\n",fn);
}

#ifdef TEST_GRAPH_BITMATRIX
//gcc -Wall -DTEST_GRAPH_BITMATRIX graph_bitmatrix.c && ./a.out
int main() {
  _graph_t g=_graph_new(10);
  _graph_set(g,0,1);
  _graph_set(g,0,2);
  _graph_set(g,0,3);
  _graph_set(g,0,6);
  _graph_set(g,4,8);
  printf("degree(0)=%u\n",_graph_degree(g,10,0));
  printf("# nodes f degree 0=%u\n",_graph_deg_k_count(g,10,0));
  printf("# nodes f degree 1=%u\n",_graph_deg_k_count(g,10,0));
  printf("# nodes f degree 3=%u\n",_graph_deg_k_count(g,10,3));
  printf("# nodes f degree 2=%u\n",_graph_deg_k_count(g,10,2));
  printf("# nodes f degree 4=%u\n",_graph_deg_k_count(g,10,4));
  _graph_show(g,10);
  _graph_write_dotfile("foo.dot",g,10);
  _graph_free(g,10);
  return 0;
}
#endif

graph_t graph_new(unsigned int nnodes) {
  graph_t g=malloc(sizeof(graph_t));
  g->g=_graph_new(nnodes);
  g->nnodes=nnodes;
  g->degree=calloc(nnodes,sizeof(unsigned int));
  g->degree_dist=calloc(nnodes,sizeof(unsigned int));
  g->degree_dist[0]=nnodes;
  return g;
}

void graph_clear(graph_t g) { // TODO
}

void graph_set_edge(graph_t g, unsigned int i, unsigned int j) {
  if (_graph_get(g->g,i,j)) return;  // no change
  _graph_set(g->g,i,j);
  unsigned int di=++g->degree[i],dj=++g->degree[j];
  g->degree_dist[di]++;
  g->degree_dist[dj]++;
  g->degree_dist[di-1]--;
  g->degree_dist[dj-1]--;
}

void graph_del_edge(graph_t g, unsigned int i, unsigned int j) {
  if (_graph_get(g->g,i,j)==0) return;  // no change
  _graph_del(g->g,i,j);
  unsigned int di=--g->degree[i],dj=--g->degree[j];
  g->degree_dist[di]++;
  g->degree_dist[dj]++;
  g->degree_dist[di+1]--;
  g->degree_dist[dj+1]--;
}

int graph_flip_edge(graph_t g, unsigned int i, unsigned int j) {
  unsigned int di=g->degree[i],dj=g->degree[j];
  int e=_graph_get(g->g,i,j)?-1:1;
  _graph_flip(g->g,i,j);
  g->degree_dist[di]--;
  g->degree_dist[dj]--;
  g->degree_dist[g->degree[i]+=e]++;
  g->degree_dist[g->degree[j]+=e]++;
  return 0;
}

node_t graph_get_nedges(graph_t g) {
  unsigned int i,n=0;
  for (i=0; i<g->nnodes; i++) n+=g->degree[i];
  return n/2;
}

void graph_show_degrees(graph_t g) {
  unsigned int i;
  for (i=0; i<g->nnodes; i++)
    printf("deg(%u)=%u\n",i,g->degree[i]);
}

void graph_show_degree_dist(graph_t g) {
  unsigned int i;
  for (i=0; i<g->nnodes; i++)
    printf("degree %2u: %u nodes\n",i,g->degree_dist[i]);
}
