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

#ifndef POTTSMODEL_H
#define POTTSMODEL_H

#include "NetDataTypes.h"

#include "igraph_types.h"
#include "igraph_vector.h"
#include "igraph_matrix.h"

// Simple matrix class with heap allocation, allowing mat[i][j] indexing.
class SimpleMatrix {
    double *data;
    size_t n;

public:
    explicit SimpleMatrix(size_t n_) : n(n_) { data = new double[n*n]; }
    ~SimpleMatrix() { delete [] data; }

    // Return a pointer to the i'th column, which can be indexed into using a second [] operator.
    // We assume column-major storage.
    double *operator [] (size_t i) { return &(data[n*i]); }
};

class PottsModel {
private:
    //  HugeArray<double> neg_gammalookup;
    //  HugeArray<double> pos_gammalookup;
    DL_Indexed_List<unsigned int*> *new_spins;
    DL_Indexed_List<unsigned int*> *previous_spins;
    HugeArray<HugeArray<double>*> correlation;
    network *net;
    unsigned int q;
    unsigned int operation_mode;
    // FILE *Qfile, *Magfile;
    SimpleMatrix Qmatrix;
    double* Qa;
    double* weights;
    double total_degree_sum;
    unsigned long num_of_nodes;
    unsigned long num_of_links;
    unsigned long k_max;
    double energy;
    double acceptance;
    double *neighbours;
public:
    PottsModel(network *net, unsigned int q, int norm_by_degree);
    ~PottsModel();
    double* color_field;
    unsigned long assign_initial_conf(int spin);
    unsigned long initialize_lookup(double kT, double gamma);
    double initialize_Qmatrix();
    double calculate_Q();
    double calculate_genQ(double gamma);
    double FindStartTemp(double gamma, double prob,  double ts);
    long   HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps);
    double HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps);
    long   HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps);
    double HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps);
    double GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1);
    double GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1);
    // long   WriteCorrelationMatrix(char *filename);
    double calculate_energy(double gamma);
    long   WriteClusters(igraph_real_t *modularity,
                         igraph_real_t *temperature,
                         igraph_vector_t *csize, igraph_vector_t *membership,
                         double kT, double gamma);
    // long   WriteSoftClusters(char *filename, double threshold);
    double Get_Energy() const {
        return energy;
    }
    double FindCommunityFromStart(double gamma, double prob, char *nodename,
                                  igraph_vector_t *result,
                                  igraph_real_t *cohesion,
                                  igraph_real_t *adhesion,
                                  igraph_integer_t *inner_links,
                                  igraph_integer_t *outer_links);
};


class PottsModelN {
private:
    //  HugeArray<double> neg_gammalookup;
    //  HugeArray<double> pos_gammalookup;
    // DL_Indexed_List<unsigned int*> *new_spins;
    // DL_Indexed_List<unsigned int*> *previous_spins;
    HugeArray<HugeArray<double>*> correlation;
    network *net;

    unsigned int q; //number of communities
    double m_p; //number of positive ties (or sum of degrees), this equals the number of edges only if it is undirected and each edge has a weight of 1
    double m_n; //number of negative ties (or sum of degrees)
    unsigned int num_nodes; //number of nodes
    bool is_directed;

    bool is_init;

    double *degree_pos_in; //Postive indegree of the nodes (or sum of weights)
    double *degree_neg_in; //Negative indegree of the nodes (or sum of weights)
    double *degree_pos_out; //Postive outdegree of the nodes (or sum of weights)
    double *degree_neg_out; //Negative outdegree of the nodes (or sum of weights)

    double *degree_community_pos_in; //Positive sum of indegree for communities
    double *degree_community_neg_in; //Negative sum of indegree for communities
    double *degree_community_pos_out; //Positive sum of outegree for communities
    double *degree_community_neg_out; //Negative sum of outdegree for communities

    unsigned int *csize; //The number of nodes in each community
    unsigned int *spin; //The membership of each node

    double *neighbours; //Array of neighbours of a vertex in each community
    double *weights; //Weights of all possible transitions to another community

public:
    PottsModelN(network *n, unsigned int num_communities, bool directed);
    ~PottsModelN();
    void assign_initial_conf(bool init_spins);
    double FindStartTemp(double gamma, double lambda, double ts);
    double HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps);
    // double HeatBathJoin(double gamma, double lambda);
    // double HeatBathLookupZeroTemp(double gamma, double lambda, unsigned int max_sweeps);
    long 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);
};

#endif
