/* -*- 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

*/

/* The original version of this file was written by Jörg Reichardt 
   This file was modified by Vincent Traag
   The original copyright notice follows here */

/***************************************************************************
                          pottsmodel.cpp  -  description
                             -------------------
    begin                : Fri May 28 2004
    copyright            : (C) 2004 by 
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include "pottsmodel_2.h"
#include "NetRoutines.h"

using namespace std;

#include "igraph_random.h"
#include "igraph_interrupt_internal.h"
#include "config.h"

//#################################################################################################
PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : acceptance(0)
{
  DLList_Iter<NNode*> iter;
  NNode *n_cur;
  unsigned int *i_ptr;
  net=n;
  q=qvalue;
  operation_mode=m;
  k_max=0;
  //needed in calculating modularity
  Qa     =new double[q+1];
  //weights for each spin state needed in Monte Carlo process
  weights=new double[q+1];
  //bookkeeping of occupation numbers of spin states or the number of links in community
  color_field=new double[q+1];
  neighbours=new double[q+1];

  num_of_nodes=net->node_list->Size();
  num_of_links=net->link_list->Size();
  
  n_cur=iter.First(net->node_list);
  //these lists are needed to keep track of spin states for parallel update mode
  new_spins=new DL_Indexed_List<unsigned int*>();
  previous_spins=new DL_Indexed_List<unsigned int*>();
  while (!iter.End())
  {
    if (k_max<n_cur->Get_Degree()) k_max=n_cur->Get_Degree();
    i_ptr=new unsigned int;
    *i_ptr=0;
    new_spins->Push(i_ptr);
    i_ptr=new unsigned int;
    *i_ptr=0;
    previous_spins->Push(i_ptr);
    n_cur=iter.Next();
  }
  return;
}
//#######################################################
//Destructor of PottsModel
//########################################################
PottsModel::~PottsModel()
{
  /* The DLItem destructor does not delete its item currently, 
     because of some bad design. As a workaround, we delete them here
     by hand */
  new_spins->delete_items();
  previous_spins->delete_items();
  delete new_spins;
  delete previous_spins;
  delete [] Qa;
  delete [] weights;
  delete [] color_field;
  delete [] neighbours;
  return;
}
//#####################################################
//Assing an initial random configuration of spins to nodes
//if called with negative argument or the spin used as argument
//when called with positve one.
//This may be handy, if you want to warm up the network.
//####################################################
unsigned long PottsModel::assign_initial_conf(int spin)
{
  int s;
  DLList_Iter<NNode*> iter;
  DLList_Iter<NLink*> l_iter;
  NNode *n_cur;
  NLink *l_cur;
  double sum_weight;
  double av_k_squared=0.0;
  double av_k=0.0;
//   printf("Assigning initial configuration...\n");
  // initialize colorfield
  for (unsigned int i=0; i<=q; i++) color_field[i]=0.0;
  //
  total_degree_sum=0.0;
  n_cur=iter.First(net->node_list);
  while (!iter.End())
  {
    if (spin<0) s=RNG_INTEGER(1,q); else s=spin;
    n_cur->Set_ClusterIndex(s);
      l_cur=l_iter.First(n_cur->Get_Links());
      sum_weight=0;
      while (!l_iter.End())
      {
        sum_weight+=l_cur->Get_Weight();   //weight should be one, in case we are not using it.
        l_cur=l_iter.Next();
      }
      // we set the sum of the weights or the degree as the weight of the node, this way
      // we do not have to calculate it again.
      n_cur->Set_Weight(sum_weight);
      av_k_squared+=sum_weight*sum_weight;
      av_k+=sum_weight;

    // in case we want all links to be contribute equally - parameter gamm=fixed
    if (operation_mode==0) {
      color_field[s]++;
    } else {
      color_field[s]+=sum_weight;
    }
    // or in case we want to use a weight of each link that is proportional to k_i\times k_j
      total_degree_sum+=sum_weight;
    n_cur=iter.Next();
  }
  av_k_squared/=double(net->node_list->Size());
          av_k/=double(net->node_list->Size());
  // total_degree_sum-=av_k_squared/av_k;
//   printf("Total Degree Sum=2M=%f\n",total_degree_sum);
  return net->node_list->Size();
}
//#####################################################################
//If I ever manage to write a decent LookUp function, it will be here
//#####################################################################
unsigned long PottsModel::initialize_lookup(double kT, double gamma)
{
  IGRAPH_UNUSED(kT); 
  IGRAPH_UNUSED(gamma);
  /*
  double beta;
  // the look-up table contains all entries of exp(-beta(-neighbours+gamma*h))
  // as needed in the HeatBath algorithm
  beta=1.0/kT;
  for (long w=0; w<=k_max+num_of_nodes; w++)
  {
     neg_lookup[w]=exp(-beta*-w
  }
  delta_ij[0]=1.0;
  for (long w=-num_of_nodes-k_max; w<=k_max+num_of_nodes; w++)
  {

  }

  // wenn wir spaeter exp(-1/kT*gamma*(nk+1-nj) fuer eine spin-flip von j nach k benoetigen schauen wir nur noch hier nach
  for (unsigned long n=1; n<=num_of_nodes; n++)
  {
    gamma_term[n]=exp(-double(n)/kT*gamma);
  }
  gamma_term[0]=1.0;
  */
  return 1;
}
//#####################################################################
// Q denotes the modulary of the network
// This function calculates it initially 
// In the event of a spin changing its state, it only needs updating
// Note that Qmatrix and Qa are only counting! The normalization 
// by num_of_links is done later
//####################################################################
double PottsModel::initialize_Qmatrix(void)
{
  DLList_Iter<NLink*> l_iter;
  NLink *l_cur;
  unsigned int i,j;
  //initialize with zeros
  num_of_links=net->link_list->Size();
  for (i=0; i<=q; i++)
  {
      Qa[i]=0.0;
      for (j=i; j<=q; j++) {
          Qmatrix[i][j]=0.0;
          Qmatrix[j][i]=0.0;
      }
  }
  //go over all links and make corresponding entries in Q matrix
  //An edge connecting state i wiht state j will get an entry in Qij and Qji
  l_cur=l_iter.First(net->link_list);
  while (!l_iter.End())
  {
    i=l_cur->Get_Start()->Get_ClusterIndex();
    j=l_cur->Get_End()->Get_ClusterIndex();
    //printf("%d %d\n",i,j);
    Qmatrix[i][j]+=l_cur->Get_Weight();
    Qmatrix[j][i]+=l_cur->Get_Weight();

    l_cur=l_iter.Next();
  }
  //Finally, calculate sum over rows and keep in Qa
  for (i=0; i<=q; i++)
  {
     for (j=0; j<=q; j++) Qa[i]+=Qmatrix[i][j];
  }
  return calculate_Q();
}
//####################################################################
// This function does the actual calculation of Q from the matrix 
// The normalization by num_of_links is done here
//####################################################################
double PottsModel::calculate_Q()
{
  double Q=0.0;
  for (unsigned int i=0; i<=q; i++)
  {
    Q+=Qmatrix[i][i]-Qa[i]*Qa[i]/double(2.0*net->sum_weights);
    if ((Qa[i]<0.0) || Qmatrix[i][i]<0.0) {
//         printf("Negatives Qa oder Qii\n\n\n");
        //printf("Press any key to continue\n\n");
        //cin >> Q;
    }
  }
  Q/=double(2.0*net->sum_weights);
  return Q;
}
double PottsModel::calculate_genQ(double gamma)
{
  double Q=0.0;
  for (unsigned int i=0; i<=q; i++)
  {
    Q+=Qmatrix[i][i]-gamma*Qa[i]*Qa[i]/double(2.0*net->sum_weights);
    if ((Qa[i]<0.0) || Qmatrix[i][i]<0.0) {
//         printf("Negatives Qa oder Qii\n\n\n");
        //printf("Press any key to continue\n\n");
        //cin >> Q;
    }
  }
  Q/=double(2.0*net->sum_weights);
  return Q;
}
//#######################################################################
// This function calculates the Energy for the standard Hamiltonian
// given a particular value of gamma and the current spin states
// #####################################################################
double PottsModel::calculate_energy(double gamma)
{
  double e=0.0;
  DLList_Iter<NLink*> l_iter;
  NLink *l_cur;
  l_cur=l_iter.First(net->link_list);
  //every in-cluster edge contributes -1
  while (!l_iter.End())
  {
    if (l_cur->Get_Start()->Get_ClusterIndex()==l_cur->Get_End()->Get_ClusterIndex()) e--;;
   l_cur=l_iter.Next();
  }
  //and the penalty term contributes according to cluster sizes
  for (unsigned int i=1; i<=q; i++)
  {
      e+=gamma*0.5*double(color_field[i])*double((color_field[i]-1));
  }
  energy=e;
  return e;
}
//##########################################################################
// We would like to start from a temperature with at least 95 of all proposed 
// spin changes accepted in 50 sweeps over the network
// The function returns the Temperature found
//#########################################################################
double PottsModel::FindStartTemp(double gamma, double prob, double ts)
{
  double kT;
  kT=ts;
  //assing random initial condition
  assign_initial_conf(-1);
  //initialize Modularity matrix, from now on, it will be updated at every spin change
  initialize_Qmatrix();
  // the factor 1-1/q is important, since even, at infinite temperature,
  // only 1-1/q of all spins do change their state, since a randomly chooses new
  // state is with prob. 1/q the old state.
  while (acceptance<(1.0-1.0/double(q))*0.95)      //want 95% acceptance
  {
       kT=kT*1.1;
       // if I ever have a lookup table, it will need initialization for every kT
       //initialize_lookup(kT,k_max,net->node_list->Size());
       HeatBathParallelLookup(gamma,prob, kT,50);
//        printf("kT=%f acceptance=%f\n", kT, acceptance);
  }
  kT*=1.1; // just to be sure...
//   printf("Starting with acceptance ratio: %1.6f bei kT=%2.4f\n",acceptance,kT);
  return kT;
}

//##############################################################
//This function does a parallel update at zero T
//Hence, it is really fast on easy problems
//max sweeps is the maximum number of sweeps it should perform,
//if it does not converge earlier
//##############################################################
long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps)
{
  DLList_Iter<NNode*> iter, net_iter;
  DLList_Iter<NLink*> l_iter;
  DLList_Iter<unsigned int*> i_iter, i_iter2;
  NNode *node, *n_cur;
  NLink *l_cur;
  unsigned int *SPIN, *P_SPIN, new_spin, spin_opt, old_spin, spin, sweep;
  // long h; // degree;
  unsigned long changes;
  double h, delta=0, deltaE, deltaEmin, w, degree;
  //HugeArray<double> neighbours;
  bool cyclic=0;
  
  sweep=0;
  changes=1;  
  while (sweep<max_sweeps && changes)
  {
    cyclic=true;
    sweep++;
    changes=0;
    //Loop over all nodes
    node=net_iter.First(net->node_list);
    SPIN=i_iter.First(new_spins);
    while (!net_iter.End())
    {
      // How many neigbors of each type?
      // set them all zero
      for (unsigned int i=0; i<=q; i++) neighbours[i]=0;
      degree=node->Get_Weight();
      //Loop over all links (=neighbours)
      l_cur=l_iter.First(node->Get_Links());
      while (!l_iter.End())
      {
        //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
	w=l_cur->Get_Weight();
	if (node==l_cur->Get_Start()) {
	  n_cur=l_cur->Get_End();
	} else { 
	  n_cur=l_cur->Get_Start(); 
	}
        neighbours[n_cur->Get_ClusterIndex()]+=w;
        l_cur=l_iter.Next();
      }
      //Search optimal Spin      
      old_spin=node->Get_ClusterIndex();
      //degree=node->Get_Degree();
      switch (operation_mode) {
      case 0: { 
	delta=1.0; 
	break;
      } 
      case 1: { //newman modularity 
	prob=degree/total_degree_sum; 
	delta=degree; 
	break;
       }
      }


      spin_opt=old_spin;
      deltaEmin=0.0;
      for (spin=1; spin<=q; spin++)  // all possible spin states
      {
          if (spin!=old_spin)
          {
            h=color_field[spin]+delta-color_field[old_spin];
            deltaE=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h);
            if (deltaE<deltaEmin) {
              spin_opt=spin;
              deltaEmin=deltaE;
            }
          }
      } // for spin

     //Put optimal spin on list for later update 
     *SPIN=spin_opt;     
     node=net_iter.Next();
     SPIN=i_iter.Next();
    } // while !net_iter.End()

    //-------------------------------
    //Now set all spins to new values
    node=net_iter.First(net->node_list);
    SPIN=i_iter.First(new_spins);
    P_SPIN=i_iter2.First(previous_spins);    
    while (!net_iter.End())
    {
      old_spin=node->Get_ClusterIndex();
      new_spin=*SPIN;
      if (new_spin!=old_spin) // Do we really have a change??
      {
        changes++;
        node->Set_ClusterIndex(new_spin);
	//this is important!!
	//In Parallel update, there occur cyclic attractors of size two
	//which then make the program run for ever
        if (new_spin!=*P_SPIN) cyclic=false;
        *P_SPIN=old_spin;
        color_field[old_spin]--;
        color_field[new_spin]++;

        //Qmatrix update
        //iteration over all neighbours
        l_cur=l_iter.First(node->Get_Links());
        while (!l_iter.End())
        {
	   w=l_cur->Get_Weight();
	   if (node==l_cur->Get_Start()) {
	     n_cur=l_cur->Get_End();
	   } else { 
	     n_cur=l_cur->Get_Start(); 
	   }
          Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w;
          Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w;
          Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w;
          Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w;
          Qa[old_spin]-=w;
          Qa[new_spin]+=w;
          l_cur=l_iter.Next();
        }  // while l_iter
      }
      node=net_iter.Next();
      SPIN=i_iter.Next();
      P_SPIN=i_iter2.Next();
    } // while (!net_iter.End())
  }  // while markov

  // In case of a cyclic attractor, we want to interrupt
  if (cyclic)  {
//       printf("Cyclic attractor!\n");
      acceptance=0.0;
      return 0;
  } else {
    acceptance=double(changes)/double(num_of_nodes);
    return changes;
  }
}
//###################################################################################
//The same function as before, but rather than parallel update, it pics the nodes to update 
//randomly
//###################################################################################
double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps)
{
  DLList_Iter<NNode*> iter;
  DLList_Iter<NLink*> l_iter;
  DLList_Iter<unsigned int*> i_iter, i_iter2;
  NNode *node, *n_cur;
  NLink *l_cur;
  unsigned int new_spin, spin_opt, old_spin, spin, sweep;
  long r;// degree;
  unsigned long changes;
  double delta=0, h, deltaE, deltaEmin,w,degree;
  //HugeArray<int> neighbours;

  sweep=0;
  changes=0;
  while (sweep<max_sweeps)
  {
    sweep++;
    //ueber alle Knoten im Netz
    for (unsigned long n=0; n<num_of_nodes; n++)
    {
      r=-1;
      while ((r<0) || (r>(long)num_of_nodes-1))
	r=RNG_INTEGER(0,num_of_nodes-1);
      /* r=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0)));*/
      node=net->node_list->Get(r);
      // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind
      // erst mal alles Null setzen
      for (unsigned int i=0; i<=q; i++) neighbours[i]=0;
      degree=node->Get_Weight();
      //Loop over all links (=neighbours)
      l_cur=l_iter.First(node->Get_Links());
      while (!l_iter.End())
      {
        //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
	w=l_cur->Get_Weight();
	if (node==l_cur->Get_Start()) {
	  n_cur=l_cur->Get_End();
	} else { 
	  n_cur=l_cur->Get_Start(); 
	}
        neighbours[n_cur->Get_ClusterIndex()]+=w;
        l_cur=l_iter.Next();
      }
      //Search optimal Spin      
      old_spin=node->Get_ClusterIndex();
      //degree=node->Get_Degree();
      switch (operation_mode) {
      case 0: { 
	delta=1.0; 
	break;
      } 
      case 1: { //newman modularity 
	prob=degree/total_degree_sum; 
	delta=degree; 
	break;
       }
      }


      spin_opt=old_spin;
      deltaEmin=0.0;
      for (spin=1; spin<=q; spin++)  // alle moeglichen Spins
      {
          if (spin!=old_spin)
          {
            h=color_field[spin]+delta-color_field[old_spin];
            deltaE=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h);
            if (deltaE<deltaEmin) {
              spin_opt=spin;
              deltaEmin=deltaE;
            }
          }
      } // for spin

      //-------------------------------
      //Now update the spins
      new_spin=spin_opt;
      if (new_spin!=old_spin) // Did we really change something??
      {
        changes++;
        node->Set_ClusterIndex(new_spin);
        color_field[old_spin]-=delta;
        color_field[new_spin]+=delta;

        //Qmatrix update
        //iteration over all neighbours
        l_cur=l_iter.First(node->Get_Links());
        while (!l_iter.End())
        {
	   w=l_cur->Get_Weight();
	   if (node==l_cur->Get_Start()) {
	     n_cur=l_cur->Get_End();
	   } else { 
	     n_cur=l_cur->Get_Start(); 
	   }
          Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w;
          Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w;
          Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w;
          Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w;
          Qa[old_spin]-=w;
          Qa[new_spin]+=w;
          l_cur=l_iter.Next();
        }  // while l_iter
       }
    } // for n
  }  // while markov

  acceptance=double(changes)/double(num_of_nodes)/double(sweep);
  return acceptance;
}
//#####################################################################################
//This function performs a parallel update at Terperature T
//#####################################################################################
long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps)
{
  DLList_Iter<NNode*> iter, net_iter;
  DLList_Iter<NLink*> l_iter;
  DLList_Iter<unsigned int*> i_iter, i_iter2;
  NNode *node, *n_cur;
  NLink *l_cur;
  unsigned int new_spin, spin_opt, old_spin;
  unsigned int *SPIN, *P_SPIN;
  unsigned int sweep;
  long max_q;
  unsigned long changes, /*degree,*/ problemcount;
  //HugeArray<int> neighbours;
  double h, delta=0, norm, r, beta,minweight, prefac=0,w, degree;
  bool cyclic=0, found;
  unsigned long num_of_nodes;

  sweep=0;
  changes=1;
  num_of_nodes=net->node_list->Size();
  while (sweep<max_sweeps && changes)
  {
    cyclic=true;
    sweep++;
    changes=0;
    //Loop over all nodes
    node=net_iter.First(net->node_list);
    SPIN=i_iter.First(new_spins);
    while (!net_iter.End())
    {
      // Initialize neighbours and weights
      problemcount=0;
      for (unsigned int i=0; i<=q; i++) {
        neighbours[i]=0;
        weights[i]=0;
      }
      norm=0.0;
      degree=node->Get_Weight();
      //Loop over all links (=neighbours)
      l_cur=l_iter.First(node->Get_Links());
      while (!l_iter.End())
      {
        //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
	w=l_cur->Get_Weight();
	if (node==l_cur->Get_Start()) {
	  n_cur=l_cur->Get_End();
	} else { 
	  n_cur=l_cur->Get_Start(); 
	}
        neighbours[n_cur->Get_ClusterIndex()]+=w;
        l_cur=l_iter.Next();
      }
      //Search optimal Spin      
      old_spin=node->Get_ClusterIndex();
      //degree=node->Get_Degree();
      switch (operation_mode) {
      case 0: { 
	prefac=1.0; 
	delta=1.0; 
	break;
      } 
      case 1: { //newman modularity 
	prefac=1.0; 
	prob=degree/total_degree_sum; 
	delta=degree; 
	break;
       }
      }
      spin_opt=old_spin;
      beta=1.0/kT*prefac;
      minweight=0.0;
      weights[old_spin]=0.0;
      for (unsigned spin=1; spin<=q; spin++)  // loop over all possible new spins
      {
          if (spin!=old_spin) // only if we have a different than old spin!
          {
            h=color_field[spin]+delta-color_field[old_spin];
            weights[spin]=double(neighbours[old_spin]-neighbours[spin])+gamma*prob*double(h);
            if (weights[spin]<minweight) minweight=weights[spin];
          }
      }   // for spin      
      for (unsigned spin=1; spin<=q; spin++)  // loop over all possibe spins
      {
            weights[spin]-=minweight;         // subtract minweight
                                              // to avoid numerical problems with large exponents
            weights[spin]=exp(-beta*weights[spin]);
            norm+=weights[spin];
      }   // for spin

     //now choose a new spin
     r = RNG_UNIF(0, norm);
     /* norm*double(rand())/double(RAND_MAX + 1.0); */
     new_spin=1;
     found=false;
     while (!found && new_spin<=q) {
       if (r<=weights[new_spin]) {
         spin_opt=new_spin;
         found=true;
         break;
       } else r-=weights[new_spin];
       new_spin++;
     }
     if (!found) {
//         printf(".");
        problemcount++;
     }
     //Put new spin on list
     *SPIN=spin_opt;

     node=net_iter.Next();
     SPIN=i_iter.Next();
    } // while !net_iter.End()

    //-------------------------------
    //now update all spins
    node=net_iter.First(net->node_list);
    SPIN=i_iter.First(new_spins);
    P_SPIN=i_iter2.First(previous_spins);
    while (!net_iter.End())
    {
      old_spin=node->Get_ClusterIndex();
      new_spin=*SPIN;
      if (new_spin!=old_spin) // Did we really change something??
      {
        changes++;
        node->Set_ClusterIndex(new_spin);
        if (new_spin!=*P_SPIN) cyclic=false;
        *P_SPIN=old_spin;
        color_field[old_spin]-=delta;
        color_field[new_spin]+=delta;

        //Qmatrix update
        //iteration over all neighbours
        l_cur=l_iter.First(node->Get_Links());
        while (!l_iter.End())
        {
	   w=l_cur->Get_Weight();
	   if (node==l_cur->Get_Start()) {
	     n_cur=l_cur->Get_End();
	   } else { 
	     n_cur=l_cur->Get_Start(); 
	   }
          Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w;
          Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w;
          Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w;
          Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w;
          Qa[old_spin]-=w;
          Qa[new_spin]+=w;
          l_cur=l_iter.Next();
        }  // while l_iter
      }
      node=net_iter.Next();
      SPIN=i_iter.Next();
      P_SPIN=i_iter2.Next();
    } // while (!net_iter.End())

  }  // while markov
  max_q=0;
  for (unsigned int i=1; i<=q; i++) if (color_field[i]>max_q) max_q=long(color_field[i]);

  //again, we would not like to end up in cyclic attractors
  if (cyclic && changes)  {
//       printf("Cyclic attractor!\n");
      acceptance=double(changes)/double(num_of_nodes);
      return 0;
  } else {
    acceptance=double(changes)/double(num_of_nodes);
    return changes;
  }
}
//##############################################################
// This is the function generally used for optimisation, 
// as the parallel update has its flaws, due to the cyclic attractors
//##############################################################
double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps)
{
  DLList_Iter<NNode*> iter;
  DLList_Iter<NLink*> l_iter;
  DLList_Iter<unsigned int*> i_iter, i_iter2;
  NNode *node, *n_cur;
  NLink *l_cur;
  unsigned int new_spin, spin_opt, old_spin;
  unsigned int sweep;
  long max_q, rn;
  unsigned long changes, /*degree,*/ problemcount;
  double degree,w, delta=0, h;
  //HugeArray<int> neighbours;
  double norm, r, beta,minweight, prefac=0;
  bool found;
  long int num_of_nodes;
  sweep=0;
  changes=0;
  num_of_nodes=net->node_list->Size();
  while (sweep<max_sweeps)
  {
    sweep++;
    //loop over all nodes in network
    for (int n=0; n<num_of_nodes; n++)
    {
      rn=-1;
      while ((rn<0) || (rn>num_of_nodes-1))
	rn=RNG_INTEGER(0, num_of_nodes-1);
      /* rn=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0))); */
        
      node=net->node_list->Get(rn);
      // initialize the neighbours and the weights
      problemcount=0;
      for (unsigned int i=0; i<=q; i++) {
        neighbours[i]=0.0;
        weights[i]=0.0;
      }
      norm=0.0;
      degree=node->Get_Weight();
      //Loop over all links (=neighbours)
      l_cur=l_iter.First(node->Get_Links());
      while (!l_iter.End())
      {
        //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
	w=l_cur->Get_Weight();
	if (node==l_cur->Get_Start()) {
	  n_cur=l_cur->Get_End();
	} else { 
	  n_cur=l_cur->Get_Start(); 
	}
        neighbours[n_cur->Get_ClusterIndex()]+=w;
        l_cur=l_iter.Next();
      }
      
      //Look for optimal spin
      
      old_spin=node->Get_ClusterIndex();
      //degree=node->Get_Degree();
      switch (operation_mode) {
      case 0: { 
	prefac=1.0; 
	delta=1.0; 
	break;
      } 
      case 1:  {//newman modularity 
	prefac=1.0; 
	prob=degree/total_degree_sum; 
	delta=degree; 
	break;
       }
      }
      spin_opt=old_spin;
      beta=1.0/kT*prefac;
      minweight=0.0;
      weights[old_spin]=0.0;
      for (unsigned spin=1; spin<=q; spin++)  // all possible new spins
      {
          if (spin!=old_spin) // except the old one!
          {
            h=color_field[spin]-(color_field[old_spin]-delta);
            weights[spin]=neighbours[old_spin]-neighbours[spin]+gamma*prob*h;
            if (weights[spin]<minweight) minweight=weights[spin];
          }
      }   // for spin      
      for (unsigned spin=1; spin<=q; spin++)  // all possible new spins
      {
            weights[spin]-=minweight;         // subtract minweigt
                                              // for numerical stability
            weights[spin]=exp(-beta*weights[spin]);
            norm+=weights[spin];
      }   // for spin
      

     //choose a new spin
/*      r = norm*double(rand())/double(RAND_MAX + 1.0); */
     r=RNG_UNIF(0, norm);
     new_spin=1;
     found=false;
     while (!found && new_spin<=q) {
       if (r<=weights[new_spin]) {
         spin_opt=new_spin;
         found=true;
         break;
       } else r-=weights[new_spin];
       new_spin++;
     }
     if (!found) {
//         printf(".");
        problemcount++;
     }
    //-------------------------------
    //now set the new spin
    new_spin=spin_opt;
    if (new_spin!=old_spin) // Did we really change something??
    {
        changes++;
        node->Set_ClusterIndex(new_spin);
        color_field[old_spin]-=delta;
        color_field[new_spin]+=delta;

        //Qmatrix update
        //iteration over all neighbours
        l_cur=l_iter.First(node->Get_Links());
        while (!l_iter.End())
        {
	   w=l_cur->Get_Weight();
	   if (node==l_cur->Get_Start()) {
	     n_cur=l_cur->Get_End();
	   } else { 
	     n_cur=l_cur->Get_Start(); 
	   }
          Qmatrix[old_spin][n_cur->Get_ClusterIndex()]-=w;
          Qmatrix[new_spin][n_cur->Get_ClusterIndex()]+=w;
          Qmatrix[n_cur->Get_ClusterIndex()][old_spin]-=w;
          Qmatrix[n_cur->Get_ClusterIndex()][new_spin]+=w;
          Qa[old_spin]-=w;
          Qa[new_spin]+=w;
          l_cur=l_iter.Next();
        }  // while l_iter
      }
    } // for n
  }  // while markov
  max_q=0;

  for (unsigned int i=1; i<=q; i++) if (color_field[i]>max_q) max_q=long(color_field[i]+0.5);

    acceptance=double(changes)/double(num_of_nodes)/double(sweep);
    return acceptance;
}

//###############################################################################################
//# Here we try to minimize the affinity to the rest of the network
//###############################################################################################
double PottsModel::FindCommunityFromStart(double gamma, double prob, 
					  char *nodename, 
					  igraph_vector_t *result,
					  igraph_real_t *cohesion, 
					  igraph_real_t *adhesion,
					  igraph_integer_t *my_inner_links,
					  igraph_integer_t *my_outer_links) {
  DLList_Iter<NNode*> iter, iter2;
  DLList_Iter<NLink*> l_iter;
  DLList<NNode*>* to_do;
  DLList<NNode*>* community;
  NNode *start_node=0, *n_cur, *neighbor, *max_aff_node, *node;
  NLink *l_cur;
  bool found=false, add=false, remove=false;
  double degree, delta_aff_add, delta_aff_rem, max_delta_aff, Ks=0.0, Kr=0, kis, kir, w;
  long community_marker=5;
  long to_do_marker=10;
  double inner_links=0, outer_links=0, aff_r, aff_s;

  IGRAPH_UNUSED(prob);

  to_do=new DLList<NNode*>;
  community=new DLList<NNode*>;
  
  // find the node in the network
  n_cur=iter.First(net->node_list);
  while (!found && !iter.End())
  {
    if (0==strcmp(n_cur->Get_Name(),nodename)) {
      start_node=n_cur;
      found=true;
      start_node->Set_Affinity(0.0);
      community->Push(start_node);
      start_node->Set_Marker(community_marker);
      Ks=start_node->Get_Weight();
      Kr=total_degree_sum-start_node->Get_Weight();
    }
    n_cur=iter.Next();
  }
  if (!found) {
//      printf("%s not found found. Aborting.\n",nodename);
//      fprintf(file,"%s not found found. Aborting.\n",nodename);
    delete to_do;
    delete community;
    return -1;
  }
  //#############################
  // initialize the to_do list and community with the neighbours of start node
  //#############################
  neighbor=iter.First(start_node->Get_Neighbours());
  while (!iter.End()) {
//     printf("Adding node %s to comunity.\n",neighbor->Get_Name());
    community->Push(neighbor);
    neighbor->Set_Marker(community_marker);
    Ks+=neighbor->Get_Weight();
    Kr-=neighbor->Get_Weight();
    neighbor=iter.Next();
  }
  node=iter.First(community);
  while (!iter.End()) {
    //now add at the second neighbors to the to_do list
    neighbor=iter2.First(node->Get_Neighbours());
    while (!iter2.End()) {
      if ((long)neighbor->Get_Marker()!=community_marker && (long)neighbor->Get_Marker()!=to_do_marker) {
	to_do->Push(neighbor);
	neighbor->Set_Marker(to_do_marker);
// 	printf("Adding node %s to to_do list.\n",neighbor->Get_Name());
      }
      neighbor=iter2.Next();
    }
    node=iter.Next();
  }
  
  //#############
  //repeat, as long as we are still adding nodes to the communtiy
  //#############
  add=true;
  remove=true;
  while (add || remove) {
      //#############################
      //calculate the affinity changes of all nodes for adding every node in the to_do list to the community
      //##############################

      IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */

      max_delta_aff=0.0;
      max_aff_node=NULL;
      add=false;
      node=iter.First(to_do);
      while (!iter.End()) {
	//printf("Checking Links of %s\n",node->Get_Name());
	degree=node->Get_Weight();
	kis=0.0;
	kir=0.0;
	// For every of the neighbors, check, count the links to the community
	l_cur=l_iter.First(node->Get_Links());
	while (!l_iter.End())
	{
	  w=l_cur->Get_Weight();
	  if (node==l_cur->Get_Start()) {
	    n_cur=l_cur->Get_End();
	  } else { 
	    n_cur=l_cur->Get_Start(); 
	  }
	  if ((long)n_cur->Get_Marker()==community_marker) {
	    kis+=w;  //the weight/number of links to the community
	  } else {
	    kir+=w;  //the weight/number of links to the rest of the network
	  }
	  l_cur=l_iter.Next();
	}
	aff_r=kir-gamma/total_degree_sum*(Kr-degree)*degree;
	aff_s=kis-gamma/total_degree_sum*Ks*degree;
	delta_aff_add=aff_r-aff_s;
	// 	if (aff_s>=aff_r && delta_aff_add<=max_delta_aff) {
	if (delta_aff_add<=max_delta_aff) {
	    node->Set_Affinity(aff_s);
	    max_delta_aff=delta_aff_add;
	    max_aff_node=node;
	    add=true;
	}
	//printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity());
	node=iter.Next();
      }
      //################
      //calculate the affinity changes for removing every single node from the community
      //################
      inner_links=0;
      outer_links=0;
      remove=false;
      node=iter.First(community);
      while (!iter.End()) {
	//printf("Checking Links of %s\n",node->Get_Name());
	degree=node->Get_Weight();
	kis=0.0;
	kir=0.0;
	// For every of the neighbors, check, count the links to the community
	l_cur=l_iter.First(node->Get_Links());
	while (!l_iter.End())
	{
	  w=l_cur->Get_Weight();
	  if (node==l_cur->Get_Start()) {
	    n_cur=l_cur->Get_End();
	  } else { 
	    n_cur=l_cur->Get_Start(); 
	  }
	  if ((long)n_cur->Get_Marker()==community_marker) {
	    kis+=w;
	    inner_links+=w;  //summing all w gives twice the number of inner links(weights)
	  } else {
	    kir+=w;
	    outer_links+=w;
	  }
	  l_cur=l_iter.Next();
	}
// 	if (kir+kis!=degree) {  printf("error kir=%f\tkis=%f\tk=%f\n",kir,kis,degree); }
	aff_r=kir-gamma/total_degree_sum*Kr*degree;
	aff_s=kis-gamma/total_degree_sum*(Ks-degree)*degree;
	delta_aff_rem=aff_s-aff_r;
	node->Set_Affinity(aff_s);
	// we should not remove the nodes, we have just added
	if (delta_aff_rem<max_delta_aff) {
	  max_delta_aff=delta_aff_rem ;
	  max_aff_node=node;
	  remove=true;
	  add=false;
	}
	//printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity());
	node=iter.Next();
      }
      inner_links=inner_links*0.5;
      //################
      // Now check, whether we want to remove or add a node
      //################
      if (add) {
        //################
	//add the node of maximum affinity to the community
	//###############
	community->Push(max_aff_node);
	max_aff_node->Set_Marker(community_marker);
	//delete node from to_do
	to_do->fDelete(max_aff_node);
	//update the sum of degrees in the community
	Ks+=max_aff_node->Get_Weight();
	Kr-=max_aff_node->Get_Weight();
// 	printf("Adding node %s to community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff);
	//now add all neighbors of this node, that are not already 
	//in the to_do list or in the community
	neighbor=iter.First(max_aff_node->Get_Neighbours());
	while (!iter.End()) {
	  if ((long)neighbor->Get_Marker()!=community_marker && (long)neighbor->Get_Marker()!=to_do_marker) {
	    to_do->Push(neighbor);
	    neighbor->Set_Marker(to_do_marker);
	    //printf("Adding node %s to to_do list.\n",neighbor->Get_Name());
	  }
	  neighbor=iter.Next();
	}
      }
      if (remove) {
	//################
	//remove those with negative affinities
	//################
	community->fDelete(max_aff_node);
	max_aff_node->Set_Marker(to_do_marker);
	//update the sum of degrees in the community
	Ks-=max_aff_node->Get_Weight();
	Kr+=max_aff_node->Get_Weight();
	//add the node to to_do again
	to_do->Push(max_aff_node);
// 	printf("Removing node %s from community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff);
      }
      IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
  }
  //###################
  //write the node in the community to a file
  //###################
  // TODO return this instead of writing it
//   fprintf(file,"Number_of_nodes:\t%d\n",community->Size());
//   fprintf(file,"Inner_Links:\t%f\n",inner_links);
//   fprintf(file,"Outer_Links:\t%f\n",Ks-2*inner_links);
//   fprintf(file,"Cohesion:\t%f\n",inner_links-gamma/total_degree_sum*Ks*Ks*0.5);
//   fprintf(file,"Adhesion:\t%f\n",outer_links-gamma/total_degree_sum*Ks*Kr);
//   fprintf(file,"\n");
  if (cohesion) {
    *cohesion=inner_links-gamma/total_degree_sum*Ks*Ks*0.5;
  } 
  if (adhesion) {
    *adhesion=outer_links-gamma/total_degree_sum*Ks*Kr;
  }
  if (my_inner_links) {
    *my_inner_links=inner_links;
  }
  if (my_outer_links) {
    *my_outer_links=outer_links;
  }
  if (result) {
    node=iter.First(community);
    igraph_vector_resize(result, 0);
    while (!iter.End()) {
      // printf("%s in community.\n",node->Get_Name());
      // fprintf(file,"%s\t%f\n",node->Get_Name(),node->Get_Affinity());
      IGRAPH_CHECK(igraph_vector_push_back(result, node->Get_Index()));
      node=iter.Next();
    }
  }
//   printf("%d nodes in community around %s\n",community->Size(),start_node->Get_Name());
//   fclose(file);
  unsigned int size=community->Size();
  delete to_do;  
  delete community;
  return size;
}

//################################################################################################
// this Function writes the clusters to disk
//################################################################################################
long PottsModel::WriteClusters(igraph_real_t *modularity,
			       igraph_real_t *temperature,
			       igraph_vector_t *csize,
			       igraph_vector_t *membership,
			       double kT, double gamma)
{
  NNode *n_cur, *n_cur2;
  /*
  double a1,a2,a3,p,p1,p2;
  long n,N,lin,lout;
  */
  DLList_Iter<NNode*> iter, iter2;
  HugeArray<int> inner_links;
  HugeArray<int> outer_links;
  HugeArray<int> nodes;
  
  //den Header schreiben
//   p=2.0*double(num_of_links)/double(num_of_nodes)/double(num_of_nodes-1);
//   fprintf(file,"      Nodes=\t%lu\n",num_of_nodes);
//   fprintf(file,"      Links=\t%lu\n",num_of_links);
//   fprintf(file,"          q=\t%d\n",q);
//   fprintf(file,"          p=\t%f\n",p);
//   fprintf(file," Modularity=\t%f\n",calculate_Q());
//   fprintf(file,"Temperature=\t%f\n", kT);
//   fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\t<Ln(#comm.)>\n");
  
  if (temperature) { *temperature=kT; }

  if (csize || membership || modularity) {
    // TODO: count the number of clusters
    for (unsigned int spin=1; spin<=q; spin++)
      {
	inner_links[spin]=0;
	outer_links[spin]=0;
	nodes[spin]=0;
	n_cur=iter.First(net->node_list);
	while (!iter.End())
	  {
	    if (n_cur->Get_ClusterIndex()==spin)
	      {
		nodes[spin]++;
		n_cur2=iter2.First(n_cur->Get_Neighbours());
		while (!iter2.End())
		  {
		    if (n_cur2->Get_ClusterIndex()==spin) inner_links[spin]++;
		    else outer_links[spin]++;
		    n_cur2=iter2.Next();
		  }
	      }
	    n_cur=iter.Next();
	  }
      }
  }
  if (modularity) {
    *modularity=0.0;
    for (unsigned int spin=1; spin<=q; spin++) {
      if (nodes[spin]>0) {
	double t1= inner_links[spin] / net->sum_weights / 2.0;
	double t2= (inner_links[spin] + outer_links[spin]) / 
	  net->sum_weights / 2.0;
	*modularity += t1;
	*modularity -= gamma * t2 * t2;
      }
    }
  }
  if (csize) {
    igraph_vector_resize(csize, 0);
    for (unsigned int spin=1; spin<=q; spin++)
      {
	if (nodes[spin]>0)
	  {
	    inner_links[spin]/=2;
	    //    fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\n");
        /*
	    N=num_of_nodes;
	    n=nodes[spin];
	    lin=inner_links[spin];
	    lout=outer_links[spin];
	    a1=N*log((double)N)-n*log((double)n)*(N-n)*log((double)N-n);
	    if ((lin==long(n*(n-1)*0.5+0.5)) || (n==1)) a2=0.0;
	    else a2=(n*(n-1)*0.5    )*log((double)n*(n-1)*0.5    )-(n*(n-1)*0.5    )-
		   (n*(n-1)*0.5-lin)*log((double)n*(n-1)*0.5-lin)+(n*(n-1)*0.5-lin)-
		   lin*log((double)lin            )+lin;
        */

        /*
	    if ((lout==n*(N-n)) || n==N) a3=0.0;
	    else a3=(n*(N-n)     )*log((double)n*(N-n)     )-(n*(N-n))-
		   (n*(N-n)-lout)*log((double)n*(N-n)-lout)+(n*(N-n)-lout)-
		   lout*log((double)lout        )+lout;
        */

        /*
	    p1=(lin+lout)*log((double)p);
	    p2=(0.5*n*(n-1)-lin + n*(N-n)-lout)*log((double)1.0-p);
        */
	    //       fprintf(file,"%d\t%d\t%d\t%d\t%f\t%f\t%f\n",spin,nodes[spin], inner_links[spin], outer_links[spin], p_in, p_out,log_num_exp);
	    IGRAPH_CHECK(igraph_vector_push_back(csize, nodes[spin]));
	  }
      }
    //   fprintf(file,"\n");
  }
  
  //die Elemente der Cluster
  if (membership) {
    long int no=-1;
    IGRAPH_CHECK(igraph_vector_resize(membership, num_of_nodes));
    for (unsigned int spin=1; spin<=q; spin++)
      {
	if (nodes[spin]>0) {
	  no++;
	}
	n_cur=iter.First(net->node_list);
	while (!iter.End())
	  {
	    if (n_cur->Get_ClusterIndex()==spin)
	      {
		//         fprintf(file,"%d\t%s\n",spin,n_cur->Get_Name());
		VECTOR(*membership)[ n_cur->Get_Index() ]=no;
	      }
	    n_cur=iter.Next();
	  }
      }
  }
  
  return num_of_nodes;
}
//################################################################################################
//This function writes the soft clusters after a gamma sweep
//that is, it groups every node together that was found in 
// more than threshold percent together with the other node 
// in the same cluster
//################################################################################################
// Does not work at the moment !!!
//################################################################################################
// long PottsModel::WriteSoftClusters(char *filename, double threshold)
// {
//   FILE *file;
//   NNode *n_cur, *n_cur2;
//   DLList_Iter<NNode*> iter, iter2;
//   DL_Indexed_List<ClusterList<NNode*>*> *cl_list, *old_clusterlist;
//   ClusterList<NNode*> *cl_cur;
  
//   double max;
  
//   file=fopen(filename,"w");
//   if (!file) {
//     printf("Could not open %s for writing.\n",filename);
//     return -1;
//   }

//   max=correlation[0]->Get(0);
//   //printf("max=%f\n",max);
//   cl_list=new DL_Indexed_List<ClusterList<NNode*>*>();
  
//   n_cur=iter.First(net->node_list);
//   while (!iter.End())
//   {
//     cl_cur=new ClusterList<NNode*>();
//     cl_list->Push(cl_cur);
//     n_cur2=iter2.First(net->node_list);
//     while (!iter2.End())
//     {
//       if (double(correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()))/max>threshold)
//         cl_cur->Push(n_cur2);
//       n_cur2=iter2.Next();
//     }
//     n_cur=iter.Next();
//   }
//   old_clusterlist=net->cluster_list;
//   net->cluster_list=cl_list;
//   clear_all_markers(net);
//   //printf("Es gibt %d Cluster\n",cl_list->Size());
//   reduce_cliques2(net, false, 15);
//   //printf("Davon bleiben %d Cluster uebrig\n",cl_list->Size());
//   clear_all_markers(net);
//   while (net->cluster_list->Size()){
//     cl_cur=net->cluster_list->Pop();
//     while (cl_cur->Size())
//     {
//       n_cur=cl_cur->Pop();
//       fprintf(file,"%s\n",n_cur->Get_Name());
//       //printf("%s\n",n_cur->Get_Name());
//     }
//     fprintf(file,"\n");
//   }
//   net->cluster_list=old_clusterlist;
//   fclose(file);

//   return 1;
// }
//#############################################################################
// Performs a gamma sweep
//#############################################################################
double PottsModel::GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions)
{
  double stepsize;
  double kT, kT_start;
  long changes;
  double gamma, acc;
  NNode *n_cur, *n_cur2;
  DLList_Iter<NNode*> iter, iter2;
  
  stepsize=(gamma_stop-gamma_start)/double(steps);
  
  n_cur=iter.First(net->node_list);
  while (!iter.End())
  {
    correlation[n_cur->Get_Index()]=new HugeArray<double>();
    n_cur2=iter2.First(net->node_list);
    while (!iter2.End())
    {
      correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index())=0.0;
      n_cur2=iter2.Next();
    }
    n_cur=iter.Next();
  }

  for (unsigned int n=0; n<=steps; n++)
  {
    assign_initial_conf(-1);
    initialize_Qmatrix();
    gamma=gamma_start+stepsize*n;
    kT=0.5;
    acceptance=0.5;
    while (acceptance<(1.0-1.0/double(q))*0.95)     //wollen 95% Acceptance
    {
          kT*=1.1;
          //initialize_lookup(kT,kmax,net->node_list->Size());
          if (!non_parallel) HeatBathParallelLookup(gamma,prob, kT,25);
          else HeatBathLookup(gamma,prob, kT,25);
          // printf("kT=%f acceptance=%f\n", kT, acceptance);
    }
    // printf("Starting with gamma=%f\n", gamma);
    kT_start=kT;
    
    for (int i=0; i<repetitions; i++)
    {
      changes=1;
      kT=kT_start;
      assign_initial_conf(-1);
      initialize_Qmatrix();
      while ((changes>0) && (kT>0.01)) 
      {
          kT=kT*0.99;
          //initialize_lookup(kT,kmax,net->node_list->Size());
          if (!non_parallel) {
	    changes=HeatBathParallelLookup(gamma, prob, kT, 50);
              // printf("kT: %f   \t Changes %li\n",kT, changes);
          } else {
	    acc=HeatBathLookup(gamma, prob, kT, 50);
             if (acc>(1.0-1.0/double(q))*0.01) changes=1; else changes=0;
             // printf("kT: %f   Acceptance: %f\n",kT, acc);
          }
      }
      // printf("Finisched with acceptance: %1.6f bei kT=%2.4f und gamma=%2.4f\n",acceptance,kT, gamma);
//      fprintf(file,"%f\t%f\n",gamma_,acceptance);
//      fprintf(file2,"%f\t%f\n",gamma_,kT);
   //   fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5));

      //Die Correlation berechnen
      n_cur=iter.First(net->node_list);
      while (!iter.End())
      {
        n_cur2=iter2.First(net->node_list);
        while (!iter2.End())
        {
          if (n_cur->Get_ClusterIndex()==n_cur2->Get_ClusterIndex())
          {
            correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index())+=0.5;
          }
          n_cur2=iter2.Next();
        }
        n_cur=iter.Next();
      }
    } // for i
} //for n
  return kT;
}
//#############################################################################
//Performs a Gamma sweep at zero T
//#############################################################################
double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions)
{
  double stepsize;
  long changes;
  double gamma, acc;
  long runs;
  NNode *n_cur, *n_cur2;
  DLList_Iter<NNode*> iter, iter2;

  stepsize=(gamma_stop-gamma_start)/double(steps);

  n_cur=iter.First(net->node_list);
  while (!iter.End())
  {
    correlation[n_cur->Get_Index()]=new HugeArray<double>();
    n_cur2=iter2.First(net->node_list);
    while (!iter2.End())
    {
      correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index())=0.0;
      n_cur2=iter2.Next();
    }
    n_cur=iter.Next();
  }

  for (unsigned int n=0; n<=steps; n++)
  {
    assign_initial_conf(-1);
    initialize_Qmatrix();
    gamma=gamma_start+stepsize*n;
    // printf("Starting with gamma=%f\n", gamma);
    for (int i=0; i<repetitions; i++)
    {
      changes=1;
      assign_initial_conf(-1);
      initialize_Qmatrix();
      runs=0;
      while (changes>0 && runs<250)
      {
          //initialize_lookup(kT,kmax,net->node_list->Size());
          if (!non_parallel) {
	    changes=HeatBathParallelLookupZeroTemp(gamma, prob, 1);
              // printf("Changes %li\n", changes);
          } else {
            acc=HeatBathLookupZeroTemp(gamma, prob, 1);
            if (acc>(1.0-1.0/double(q))*0.01) changes=1; else changes=0;
            // printf("Acceptance: %f\n", acc);
          }
          runs++;
      }
      // printf("Finisched with Modularity: %1.6f bei Gamma=%1.6f\n",calculate_Q(), gamma);
//      fprintf(file,"%f\t%f\n",gamma_,acceptance);
//      fprintf(file2,"%f\t%f\n",gamma_,kT);
   //   fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5));

      //Die Correlation berechnen
      n_cur=iter.First(net->node_list);
      while (!iter.End())
      {
        n_cur2=iter2.First(net->node_list);
        while (!iter2.End())
        {
          if (n_cur->Get_ClusterIndex()==n_cur2->Get_ClusterIndex())
          {
            correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index())+=0.5;
            correlation[n_cur2->Get_Index()]->Set(n_cur->Get_Index())+=0.5;
          }
          n_cur2=iter2.Next();
        }
        n_cur=iter.Next();
      }
    } // for i
} //for n
  return gamma;
}
//#######################################################################
//-----------------------------------------------------------------------
//#######################################################################
// This function writes the Correlation Matrix that results from a 
// Gamma-Sweep, this matrix is used to make ps files of it.
// ######################################################################
// long PottsModel::WriteCorrelationMatrix(char *filename)
// {
//   FILE *file, *file2;
//   char filename2[255];
//   NNode *n_cur, *n_cur2;
//   DLList_Iter<NNode*> iter, iter2;

//   sprintf(filename2,"%s.mat",filename);
//   file=fopen(filename,"w");
//   if (!file) {
//     printf("Could not open %s for writing.\n",filename);
//     return -1;
//   }
//   file2=fopen(filename2,"w");
//   if (!file2) {
//     printf("Could not open %s for writing.\n",filename2);
//     return -1;
//   }
//   //write the header in one line
//   n_cur=iter.First(net->node_list);
//   while (!iter.End())
//   {  
//       fprintf(file, "\t%s",n_cur->Get_Name());
//       n_cur=iter.Next();
//   }    
//   fprintf(file, "\n");

//   //fprintf(file, "%d\t%d\n",net->node_list->Size(),net->node_list->Size());

//   long r=0,c=0;
//   n_cur=iter.First(net->node_list);
//   while (!iter.End())
//   {
//     fprintf(file, "%s",n_cur->Get_Name());
//     r++;
//     n_cur2=iter2.First(net->node_list);
//     while (!iter2.End())
//     {
//       c++;
//       fprintf(file,"\t%f",correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()));
//       fprintf(file2,"%li\t%li\t%f\n",r,c,correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()));
//       n_cur2=iter2.Next();
//     }
//     fprintf(file,"\n");
//     n_cur=iter.Next();
//   }
//   fclose(file);
//   fclose(file2);
//   return 1;
// }
//##############################################################################

//#################################################################################################
PottsModelN::PottsModelN(network *n, unsigned int num_communities, bool directed)
{
	//Set internal variable
	net	= n;
	q	= num_communities;
	
	is_directed = directed;
	
	is_init = false;
	
	num_nodes	= net->node_list->Size();
}
//#######################################################
//Destructor of PottsModel
//########################################################
PottsModelN::~PottsModelN()
{
	delete degree_pos_in;
	delete degree_neg_in;
	delete degree_pos_out;
	delete degree_neg_out;
	
	delete degree_community_pos_in;
	delete degree_community_neg_in;	
	delete degree_community_pos_out;
	delete degree_community_neg_out;
	
	delete weights;
	delete neighbours;
	delete csize;
	
	delete spin;
	
	return;
}

void PottsModelN::assign_initial_conf(bool init_spins)
{
	#ifdef DEBUG
	printf("Start assigning.\n");
	#endif
	int s;
	DLList_Iter<NNode*> iter;
	DLList_Iter<NLink*> l_iter;
	NNode *n_cur;
	NLink *l_cur;
	

	if(init_spins)
	{
		#ifdef DEBUG
		printf("Initializing spin.\n");
		#endif
		//Bookkeeping of the various degrees (positive/negative) and (in/out)
		degree_pos_in	= new double[num_nodes]; //Postive indegree of the nodes (or sum of weights)
		degree_neg_in	= new double[num_nodes]; //Negative indegree of the nodes (or sum of weights)
		degree_pos_out	= new double[num_nodes]; //Postive outdegree of the nodes (or sum of weights)
		degree_neg_out	= new double[num_nodes]; //Negative outdegree of the nodes (or sum of weights)	
				
		spin			= new unsigned int[num_nodes]; //The spin state of each node
	}
	
	if (is_init)
	{
		delete degree_community_pos_in;
		delete degree_community_neg_in;
		delete degree_community_pos_out;
		delete degree_community_neg_out;
		
		delete weights;
		delete neighbours;
		delete csize;
	}
	
	is_init = true;
	
	//Bookkeep of occupation numbers of spin states or the number of links in community...
	degree_community_pos_in		= new double[q+1]; //Positive sum of indegree for communities
	degree_community_neg_in		= new double[q+1]; //Negative sum of indegree for communities
	degree_community_pos_out	= new double[q+1];//Positive sum of outegree for communities
	degree_community_neg_out	= new double[q+1]; //Negative sum of outdegree for communities	

	//...and of weights and neighbours for in the HeathBathLookup
	weights						= new double[q+1]; //The weights for changing to another spin state
	neighbours					= new double[q+1]; //The number of neighbours (or weights) in different spin states
	csize						= new unsigned int[q+1]; //The number of nodes in each community	
	

	//Initialize communities
	for (unsigned int i=0; i<=q; i++) 
	{
		degree_community_pos_in[i]	= 0.0;
		degree_community_neg_in[i]	= 0.0;	
		degree_community_pos_out[i]	= 0.0;
		degree_community_neg_out[i]	= 0.0;
		
		csize[i]					= 0;
	}
	
	//Initialize vectors
	if (init_spins)
	{	
		for (unsigned int i = 0; i < num_nodes; i++)
		{
			degree_pos_in[i]	= 0.0;
			degree_neg_in[i]	= 0.0;
			degree_pos_out[i]	= 0.0;
			degree_neg_out[i]	= 0.0;
			
			#ifdef DEBUG
			printf("Initializing spin %d", i);
			#endif
			spin[i]	= 0;
		}
	}
	m_p=0.0;
	m_n=0.0;
	//Set community for each node, and 
	//correctly store it in the bookkeeping
	
	double sum_weight_pos_in, sum_weight_pos_out, sum_weight_neg_in, sum_weight_neg_out;
	//double av_w = 0.0, av_k=0.0;
	//int l = 0;
	#ifdef DEBUG
	printf("Visiting each node.\n");
	#endif
	for (unsigned int v = 0; v < num_nodes; v++)
	{
		if (init_spins)
		{
			s = RNG_INTEGER(1, q);  //The new spin s
			spin[v] = (unsigned int)s;
		}
		else
			s = spin[v];
		
		#ifdef DEBUG		
		printf("Spin %d assigned to node %d.\n", s, v);
		#endif
			
		n_cur				=  net->node_list->Get(v);
		
		l_cur				= l_iter.First(n_cur->Get_Links());
		
		sum_weight_pos_in	= 0.0;
		sum_weight_pos_out	= 0.0;
		sum_weight_neg_in	= 0.0;
		sum_weight_neg_out	= 0.0;
		
		while (!l_iter.End())
		{
			double w = l_cur->Get_Weight();
			//av_w = (av_w*l + w)/(l+1); //Average weight
			//l++;
			if (l_cur->Get_Start() == n_cur) //From this to other, so outgoing link
				if (w > 0)
					sum_weight_pos_out += w;   //Increase positive outgoing weight
				else
					sum_weight_neg_out -= w;	//Increase negative outgoing weight
			else
				if (w > 0)
					sum_weight_pos_in += w;   //Increase positive incoming weight
				else
					sum_weight_neg_in -= w;	//Increase negative incoming weight			
			
			l_cur=l_iter.Next();
		}
		
		if (!is_directed)
		{
			double sum_weight_pos		= sum_weight_pos_out + sum_weight_pos_in; 
				   sum_weight_pos_out	= sum_weight_pos; 
				   sum_weight_pos_in	= sum_weight_pos;
			double sum_weight_neg = sum_weight_neg_out + sum_weight_neg_in;
				   sum_weight_neg_out	= sum_weight_neg; 
				   sum_weight_neg_in	= sum_weight_neg;			
		}
		
		//av_k = (av_k*l + sum_weight_pos_in)/(l+1); //Average k
		
		if (init_spins)
		{
			//Set the degrees correctly
			degree_pos_in[v]	= sum_weight_pos_in;
			degree_neg_in[v]	= sum_weight_neg_in;
			degree_pos_out[v]	= sum_weight_pos_out;
			degree_neg_out[v]	= sum_weight_neg_out;
		}
		
		//Correct the community bookkeeping
		degree_community_pos_in[s]	+= sum_weight_pos_in;
		degree_community_neg_in[s]	+= sum_weight_neg_in;
		degree_community_pos_out[s]	+= sum_weight_pos_out;
		degree_community_neg_out[s]	+= sum_weight_neg_out;
		
		//Community just increased
		csize[s]++;
	
		//Sum the weights (notice that sum of indegrees equals sum of outdegrees)
		m_p += sum_weight_pos_in;
		m_n += sum_weight_neg_in;
	}
	
	#ifdef DEBUG
	printf("Done assigning.\n");
	#endif

	return;
}
//##############################################################
// This is the function generally used for optimisation, 
// as the parallel update has its flaws, due to the cyclic attractors
//##############################################################
double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps)
{
	#ifdef DEBUG
	printf("Starting sweep at temperature %f.\n", t);
	#endif
	DLList_Iter<NNode*> iter;
	DLList_Iter<NLink*> l_iter;
	DLList_Iter<unsigned int*> i_iter, i_iter2;
	NNode *node, *n_cur;
	NLink *l_cur;
	/* The new_spin contains the spin to which we will update,
	 * the spin_opt is the optional spin we will consider and
	 * the old_spin is the spin of the node we are currently
	 * changing.
	 */  
	unsigned int new_spin, spin_opt, old_spin;
	unsigned int sweep; //current sweep
	unsigned long changes, problemcount; //Number of changes and number of problems encountered

	double exp_old_spin; //The expectation value for the old spin
	double exp_spin; //The expectation value for the other spin(s)
	int v; //The node we will be investigating
	
	//The variables required for the calculations
	double delta_pos_out, delta_pos_in, delta_neg_out, delta_neg_in;
	double k_v_pos_out, k_v_pos_in, k_v_neg_out, k_v_neg_in;
	
	//weight of edge
	double w;
	
	double beta = 1/t; //Weight for probabilities
	double r = 0.0; //random number used for assigning new spin
	
	double maxweight = 0.0;
	double sum_weights = 0.0; //sum_weights for normalizing the probabilities
	
	sweep=0;
	changes=0;
	double m_pt = m_p;
	double m_nt = m_n;
	
	if (m_pt < 0.001)
		m_pt = 1;
		
	if (m_nt < 0.001)
		m_nt = 1;
		
	while (sweep<max_sweeps)
	{
		sweep++;
		//loop over all nodes in network
		for (unsigned int n = 0; n < num_nodes; n++)
		{
			//Look for a random node
			v = RNG_INTEGER(0, num_nodes-1);
			//We will be investigating node v
			
			node=net->node_list->Get(v);
			
			/*******************************************/
			// initialize the neighbours and the weights
			problemcount=0;
			for (unsigned int i=0; i<=q; i++) {
				neighbours[i]=0.0;
				weights[i]=0.0;
			}

			//Loop over all links (=neighbours)
			l_cur=l_iter.First(node->Get_Links());
			while (!l_iter.End())
			{
				w=l_cur->Get_Weight();
				if (node==l_cur->Get_Start()) {
					n_cur=l_cur->Get_End();
				} else { 
					n_cur=l_cur->Get_Start(); 
				}
				//Add the link to the correct cluster
				neighbours[spin[n_cur->Get_Index()]]+=w;
				l_cur=l_iter.Next();
			}
			//We now have the weight of the (in and out) neighbours 
			//in each cluster available to us.
			/*******************************************/
			old_spin=spin[v];
						
			//Look for optimal spin
						
			//Set the appropriate variable
			delta_pos_out	= degree_pos_out[v];
			delta_pos_in	= degree_pos_in[v];
			delta_neg_out	= degree_neg_out[v];
			delta_neg_in	= degree_neg_in[v];
			
			k_v_pos_out		= gamma*delta_pos_out/m_pt;
			k_v_pos_in		= gamma*delta_pos_in/m_pt;
			k_v_neg_out		= lambda*delta_neg_out/m_nt;
			k_v_neg_in		= lambda*delta_neg_in/m_nt;			
			
			//The expectation value for the old spin
			if (is_directed)
				exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - 
								k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)) + 
							   (k_v_pos_in * (degree_community_pos_out[old_spin] - delta_pos_out) - 
								k_v_neg_in * (degree_community_neg_out[old_spin] - delta_neg_out));
			else
				exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) - 
								k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in));			

			/*******************************************/
			//Calculating probabilities for each transition to another 
			//community.			
			
			maxweight=0.0;
			weights[old_spin]=0.0;
														
			for (spin_opt=1; spin_opt<=q; spin_opt++)  // all possible new spins
			{
				if (spin_opt!=old_spin) // except the old one!
				{
					if (is_directed)
						exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]) + 
								   (k_v_pos_in * degree_community_pos_out[spin_opt] - k_v_neg_in * degree_community_neg_out[spin_opt]);
					else
						exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]);
						
					weights[spin_opt] = (neighbours[spin_opt] - exp_spin) - (neighbours[old_spin] - exp_old_spin);
					
					if (weights[spin_opt] > maxweight)
						maxweight = weights[spin_opt];
				}
			}   // for spin      
			
			//Calculate exp. prob. an
			sum_weights = 0.0;
			for (spin_opt=1; spin_opt<=q; spin_opt++)  // all possible new spins
			{
				weights[spin_opt] -= maxweight;  //subtract maxweight for numerical stability (otherwise overflow).
				weights[spin_opt]  = exp((double)(beta*weights[spin_opt]));
				sum_weights   += weights[spin_opt];
			}   // for spin
			/*******************************************/
						
			
			/*******************************************/
			//Choose a new spin dependent on the calculated probabilities
			r = RNG_UNIF(0, sum_weights);
			new_spin = 1;
			
			bool found = false;
			while (!found && new_spin <= q) 
			{
				if (r <= weights[new_spin]) 
				{
					spin_opt = new_spin; //We have found are new spin
					found = true;
					break;
				} 
				else 
					r -= weights[new_spin]; //Perhaps the next spin is the one we want

				new_spin++;
			}
			
			//Some weird thing happened. We haven't found a new spin
			//while that shouldn't be the case. Numerical problems?
			if (!found) 
				problemcount++;

			new_spin=spin_opt;
			//If there wasn't a problem we should have found
			//our new spin.
			/*******************************************/
			
			
			/*******************************************/
			//The new spin is available to us, so change
			//all the appropriate counters.
			if (new_spin!=old_spin) // Did we really change something??
			{
				changes++;
				spin[v] = new_spin;
				
				//The new spin increase by one, and the old spin decreases by one
				csize[new_spin]++; csize[old_spin]--;
				
				//Change the sums of degree for the old spin...		
				degree_community_pos_in[old_spin]	-= delta_pos_in;
				degree_community_neg_in[old_spin]	-= delta_neg_in;
				degree_community_pos_out[old_spin]	-= delta_pos_out;
				degree_community_neg_out[old_spin]	-= delta_neg_out;
				
				//...and for the new spin
				degree_community_pos_in[new_spin]	+= delta_pos_in;
				degree_community_neg_in[new_spin]	+= delta_neg_in;
				degree_community_pos_out[new_spin]	+= delta_pos_out;
				degree_community_neg_out[new_spin]	+= delta_neg_out;				
			}
			
			//We have no change a node from old_spin to new_spin
			/*******************************************/
		
		} // for n
	}  // while sweep
	#ifdef DEBUG
	printf("Done %d sweeps.\n", max_sweeps);
	printf("%d changes made for %d nodes.\n", changes, num_nodes);
	printf("Last node is %d and last random number is %f with sum of weights %f with spin %d.\n", v, r, sum_weights, old_spin);
	#endif
	
    return (double(changes)/double(num_nodes)/double(sweep));
}

//We need to begin at a suitable temperature. That is, a temperature at which
//enough nodes may change their initially assigned communties
double PottsModelN::FindStartTemp(double gamma, double lambda, double ts)
{
  double kT;
  kT=ts;
  //assing random initial condition
  assign_initial_conf(true);
  // the factor 1-1/q is important, since even, at infinite temperature,
  // only 1-1/q of all spins do change their state, since a randomly chooses new
  // state is with prob. 1/q the old state.
  double acceptance = 0.0;
  while (acceptance<(1.0-1.0/double(q))*0.95)      //want 95% acceptance
  {
       kT=kT*1.1;
       acceptance=HeatBathLookup(gamma,lambda, kT,50);
  }
  kT*=1.1; // just to be sure...
  return kT;
}

long PottsModelN::WriteClusters(igraph_real_t *modularity,
				igraph_real_t *temperature,
				igraph_vector_t *community_size,
				igraph_vector_t *membership,
				igraph_matrix_t *adhesion,
				igraph_matrix_t *normalised_adhesion,
				igraph_real_t *polarization,
				double t,
				double d_p,
				double d_n,
				double gamma,
				double lambda)
{
        IGRAPH_UNUSED(gamma);
	IGRAPH_UNUSED(lambda);
	#ifdef DEBUG
	printf("Start writing clusters.\n");
	#endif
	//Reassign each community so that we retrieve a community assignment 1 through num_communities	
	unsigned int *cluster_assign = new unsigned int[q+1];
	for (unsigned int i = 0; i <= q; i++)
	{
		cluster_assign[i] = 0;
	}
	
	int num_clusters = 0;
	
	//Find out what the new communities will be
	for (unsigned int i = 0; i < num_nodes; i++)
	{
		int s = spin[i];
		if (cluster_assign[s] == 0)
		{
			num_clusters++;		
			cluster_assign[s] = num_clusters;
			#ifdef DEBUG
			printf("Setting cluster %d to %d.\n", s, num_clusters);
			#endif
		}
	}
	

    /*
	DLList_Iter<NNode*> iter;
	NNode *n_cur=iter.First(net->node_list);		
	n_cur = iter.First(net->node_list);
    */

	//And now assign each node to its new community
	q = num_clusters;	
	for (unsigned int i = 0; i < num_nodes; i++)
	{
		#ifdef DEBUG
		printf("Setting node %d to %d.\n", i, cluster_assign[spin[i]]);
		#endif
		unsigned int s = cluster_assign[spin[i]];
		spin[i] = s;
		#ifdef DEBUG
		printf("Have set node %d to %d.\n", i, s);
		#endif
	}
	assign_initial_conf(false);
	
	delete[] cluster_assign;
		
	if (temperature) { *temperature=t; }
	
	if (community_size)
	{
		//Initialize the vector
		IGRAPH_CHECK(igraph_vector_resize(community_size, q));
		for (unsigned int spin_opt = 1; spin_opt <= q; spin_opt++)
		{
			//Set the community size
			VECTOR(*community_size)[spin_opt-1]=csize[spin_opt];
		}
	}
	
	//Set the membership	
	if (membership) 
	{
		IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes));		
		for (unsigned int i = 0; i < num_nodes; i++)
		{
			VECTOR(*membership)[ i ]= spin[i]-1;
		}
	}
	
	double Q = 0.0; //Modularity
	if (adhesion)
	{
		IGRAPH_CHECK(igraph_matrix_resize(adhesion, q, q));
		IGRAPH_CHECK(igraph_matrix_resize(normalised_adhesion, q, q));
		
		double **num_links_pos = 0;
		double **num_links_neg = 0;
		//memory allocated for elements of rows.
		num_links_pos = new double *[q+1] ;
		num_links_neg = new double *[q+1] ;

		//memory allocated for  elements of each column.
		for( unsigned int i = 0 ; i < q+1 ; i++)
		{
			num_links_pos[i] = new double[q+1];
			num_links_neg[i] = new double[q+1];
		}
		

		
		//Init num_links
		for (unsigned int i = 0; i <= q; i++)
		{
			for (unsigned int j = 0; j <= q; j++)
			{
				num_links_pos[i][j] = 0.0;
				num_links_neg[i][j] = 0.0;
			}
		}
		
		DLList_Iter<NLink*> iter_l;
		NLink *l_cur = iter_l.First(net->link_list);
		
		double w = 0.0;
		
		while (!iter_l.End())
		{
			w = l_cur->Get_Weight();
			unsigned int a = spin[l_cur->Get_Start()->Get_Index()];
			unsigned int b =  spin[l_cur->Get_End()->Get_Index()];
			if (w > 0)
			{
				num_links_pos[a][b] += w;
				if (!is_directed && a != b) //Only one edge is defined in case it is undirected
					num_links_pos[b][a] += w;
			}
			else
			{
				num_links_neg[a][b] -= w;
				if (!is_directed && a != b) //Only one edge is defined in case it is undirected
					num_links_neg[b][a] -= w;
			}
			
			l_cur = iter_l.Next();
		} //while links
		
		#ifdef DEBUG
		printf("d_p: %f\n", d_p);
		printf("d_n: %f\n", d_n);
		#endif
	
		double expected = 0.0;
		double a = 0.0;
		double normal_a = 0.0;
		
		double delta, u_p, u_n;
		double max_expected, max_a;
		
		//We don't take into account the lambda or gamma for
		//computing the modularity and adhesion, since they
		//are then incomparable to other definitions.
		for (unsigned int i = 1; i <= q; i++)
		{
			for (unsigned int j = 1; j <= q; j++)
			{
				if (!is_directed && i == j)
					expected	= degree_community_pos_out[i] * degree_community_pos_in[j]/(m_p == 0 ? 1 : 2*m_p)
								- degree_community_neg_out[i] * degree_community_neg_in[j]/(m_n == 0 ? 1 : 2*m_n);
				else
					expected	= degree_community_pos_out[i] * degree_community_pos_in[j]/(m_p == 0 ? 1: m_p)
								- degree_community_neg_out[i] * degree_community_neg_in[j]/(m_n == 0 ? 1 : m_n);
				
				a			= (num_links_pos[i][j] - num_links_neg[i][j]) - expected;
				
				if (i == j) //cohesion
				{
					if (is_directed)
						delta	= d_p * csize[i] * (csize[i] - 1); //Maximum amount
					else
						delta	= d_p * csize[i] * (csize[i] - 1)/2; //Maximum amount
					
					u_p		= delta - num_links_pos[i][i]; //Add as many positive links we can
					u_n		= -num_links_neg[i][i]; //Delete as many negative links we can				
					Q	   += a;
				}
				else //adhesion
				{
					if (is_directed)
						delta	= d_n * csize[i] * csize[j]*2; //Maximum amount
					else
						delta	= d_n * csize[i] * csize[j]; //Maximum amount

					u_p		= -num_links_pos[i][j]; //Delete as many positive links we can
					u_n		= delta - num_links_neg[i][j]; //Add as many negative links we can
				}
				
				if (!is_directed && i == j)
					max_expected	= (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p)/((m_p + u_p) == 0 ? 1 : 2*(m_p + u_p)) 
									- (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n)/((m_n + u_n) == 0 ? 1 : 2*(m_n + u_n));
				else
					max_expected	= (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p)/((m_p + u_p) == 0 ? 1 : m_p + u_p) 
									- (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n)/((m_n + u_n) == 0 ? 1 : m_n + u_n);
				//printf("%f/%f %d/%d\t", num_links_pos[i][j], num_links_neg[i][j], csize[i], csize[j]);
				//printf("%f/%f - %f(%f)\t", u_p, u_n, expected, max_expected);
				max_a			= ((num_links_pos[i][j] + u_p) - (num_links_neg[i][j] + u_n)) - max_expected;				
				
				
				//In cases where we haven't actually found a ground state
				//the adhesion/cohesion *might* not be negative/positive,
				//hence the maximum adhesion and cohesion might behave quite
				//strangely. In order to prevent that, we limit them to 1 in
				//absolute value, and prevent from dividing by zero (even if
				//chuck norris would).
				if (i == j)
					normal_a = a/(max_a == 0 ? a : max_a);
				else
					normal_a = -a/(max_a == 0 ? a : max_a);
					
				if (normal_a > 1)
					normal_a = 1;
				else if (normal_a < -1)
					normal_a = -1;
					
				MATRIX(*adhesion, i - 1, j - 1) = a;
				MATRIX(*normalised_adhesion, i - 1, j - 1) = normal_a;
			} //for j
			//printf("\n");
		} //for i
		
		//free the allocated memory
		for( unsigned int i = 0 ; i < q+1 ; i++ )
		{
			delete [] num_links_pos[i] ;
			delete [] num_links_neg[i];
		}
		delete [] num_links_pos ;	
		delete [] num_links_neg ;
		
	} //adhesion
	
	if (modularity)  
	{ 
		if (is_directed)
			*modularity=Q/(m_p + m_n); 
		else
			*modularity=2*Q/(m_p + m_n); //Correction for the way m_p and m_n are counted. Modularity is 1/m, not 1/2m
	}
	
	if (polarization) 
	{ 
		double sum_ad = 0.0;
		for (unsigned int i = 0; i < q; i++)
		{
			for (unsigned int j = 0; j < q; j++)
			{
				if (i != j)
				{
					sum_ad -= MATRIX(*normalised_adhesion, i, j);
				}
			}
		}
		*polarization= sum_ad/(q*q - q);
	}
	#ifdef DEBUG	
	printf("Finished writing cluster.\n");
	#endif
	return num_nodes;
}
