/* -*- mode: C -*-  */
/* 
   IGraph library.
   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
   334 Harvard street, Cambridge, MA 02139 USA
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
   02110-1301 USA

*/

#include "igraph_community.h"
#include "igraph_memory.h"
#include "igraph_interface.h"
#include "igraph_iterators.h"
#include "config.h"

/**
 * \function igraph_coreness 
 * \brief Finding the coreness of the vertices in a network.
 *
 * The k-core of a graph is a maximal subgraph in which each vertex
 * has at least degree k. (Degree here means the degree in the
 * subgraph of course.). The coreness of a vertex is the highest order 
 * of a k-core containing the vertex.
 * 
 * </para><para>
 * This function implements the algorithm presented in Vladimir
 * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores
 * Decomposition of Networks. 
 * \param graph The input graph.
 * \param cores Pointer to an initialized vector, the result of the
 *        computation will be stored here. It will be resized as
 *        needed. For each vertex it contains the highest order of a
 *        core containing the vertex.
 * \param mode For directed graph it specifies whether to calculate
 *        in-cores, out-cores or the undirected version. It is ignored
 *        for undirected graphs. Possible values: \c IGRAPH_ALL
 *        undirected version, \c IGRAPH_IN in-cores, \c IGRAPH_OUT
 *        out-cores. 
 * \return Error code.
 *
 * Time complexity: O(|E|), the number of edges.
 */

int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, 
		    igraph_neimode_t mode) {

  long int no_of_nodes=igraph_vcount(graph);
  long int *bin, *vert, *pos;
  long int maxdeg;
  long int i, j=0;
  igraph_vector_t neis;
  igraph_neimode_t omode;
  
  if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) {
    IGRAPH_ERROR("Invalid mode in k-cores", IGRAPH_EINVAL);
  }
  if (!igraph_is_directed(graph) || mode==IGRAPH_ALL) {
    mode=omode=IGRAPH_ALL;
  } else if (mode==IGRAPH_IN) {
    omode=IGRAPH_OUT;
  } else {
    omode=IGRAPH_IN;
  }

  vert=igraph_Calloc(no_of_nodes, long int);
  if (vert==0) {
    IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
  }
  IGRAPH_FINALLY(igraph_free, vert);
  pos=igraph_Calloc(no_of_nodes, long int);
  if (pos==0) {
    IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
  }
  IGRAPH_FINALLY(igraph_free, pos);

  /* maximum degree + degree of vertices */
  IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, 
			     IGRAPH_LOOPS));
  maxdeg = (long int) igraph_vector_max(cores);

  bin=igraph_Calloc(maxdeg+1, long int);
  if (bin==0) {
    IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
  }
  IGRAPH_FINALLY(igraph_free, bin);

  /* degree histogram */
  for (i=0; i<no_of_nodes; i++) {
    bin[ (long int)VECTOR(*cores)[i] ] += 1;
  }
  
  /* start pointers */
  j=0;
  for (i=0; i<=maxdeg; i++) {
    long int k=bin[i];
    bin[i] = j;
    j += k;
  }
  
  /* sort in vert (and corrupt bin) */
  for (i=0; i<no_of_nodes; i++) {
    pos[i] = bin[(long int)VECTOR(*cores)[i]];
    vert[pos[i]] = i;
    bin[(long int)VECTOR(*cores)[i]] += 1;
  }
  
  /* correct bin */
  for (i=maxdeg; i>0; i--) {
    bin[i] = bin[i-1];
  }
  bin[0]=0;

  /* this is the main algorithm */
  IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg);
  for (i=0; i<no_of_nodes; i++) {
    long int v=vert[i];
    IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, omode));
    for (j=0; j<igraph_vector_size(&neis); j++) {
      long int u=(long int) VECTOR(neis)[j];
      if (VECTOR(*cores)[u] > VECTOR(*cores)[v]) {
	long int du=(long int) VECTOR(*cores)[u];
	long int pu=pos[u];
	long int pw=bin[du];
	long int w=vert[pw];
	if (u != w) {
	  pos[u]=pw;
	  pos[w]=pu;
	  vert[pu]=w;
	  vert[pw]=u;
	}
	bin[du] += 1;
	VECTOR(*cores)[u] -= 1;
      }
    }
  }
  
  igraph_vector_destroy(&neis);
  IGRAPH_FINALLY_CLEAN(1);

  igraph_free(bin);
  igraph_free(pos);
  igraph_free(vert);
  IGRAPH_FINALLY_CLEAN(3);
  return 0;
}
