#include "defs.h"

typedef struct {
    double time;
    double modularity;
} perf_t;

char filename[FNAME_LEN];
int nIters = 4;
double modularity_reference = -1;

double modularity(graph_t &graph, vector<vertex_id_t> &component_id)
{
	const vertex_id_t n = graph.n;
	weight_t M = 0;
	
    // calculate sigma_in and sigma_t for each component	
	weight_t *in = new weight_t[n];
	weight_t *tot = new weight_t[n];
	
	memset(in,  0, sizeof(weight_t) * n);
	memset(tot, 0, sizeof(weight_t) * n);
	
	for (vertex_id_t i = 0; i < n; ++i) {
        vertex_id_t cu = component_id[i];
       	for (edge_id_t j = graph.rowsIndices[i]; j < graph.rowsIndices[i + 1]; ++j) {			
            vertex_id_t cv = component_id[graph.endV[j]];
       	    weight_t weight = graph.weights[j];
		    M += weight;
		    if (cu == cv) {
                in[cu]  += weight;
                tot[cu] += weight;
	        } else {
                tot[cu] += weight;
      	        tot[cv] += weight;
	        }
       	}
	}

    // calculate modularity    
    weight_t q = 0;
    for (vertex_id_t i = 0; i < n; ++i) 
	{
        weight_t sigma_in =  in[i];
		weight_t sigma_tot = tot[i];
        q += (sigma_in / M - (sigma_tot / M) * (sigma_tot / M));
    }

	delete []in;
	delete []tot;
	
    return q;
}

void usage(int argc, char **argv)
{
    printf("Usage:\n");
    printf("    %s -in <input> [options] \n", argv[0]);
    printf("Options:\n");
    printf("    -in <input> -- input graph filename\n");
    printf("    -nIters <nIters> -- number of iterations. By default 4\n");
    printf("    -mod_ref -- reference modularity for rating function\n");
    exit(1);
}
void init(int argc, char **argv)
{
    if (argc == 1) usage(argc, argv);
    
    for (int i = 1; i < argc; ++i) {
        if (!strcmp(argv[i], "-in")) {
            int l = strlen(argv[++i]);
            strncpy(filename, argv[i], (l > FNAME_LEN - 1 ? FNAME_LEN - 1 : l) );
        }
		if (!strcmp(argv[i], "-nIters")) {
			nIters = (int) atoi(argv[++i]);
        }
		if (!strcmp(argv[i], "-mod_ref")) {
			modularity_reference = atof(argv[++i]);
        }
    }
}

int main(int argc, char **argv)
{
    init(argc, argv);
    graph_t graph;
    // read the graph
    readGraph(&graph, filename);

    struct timespec start_ts_build, finish_ts_build;
    double build_time;

    clock_gettime(CLOCK, &start_ts_build);

    // ---------------------------------
    // you can change this code fragment
    // ----------------------------------
    // prepare internal graph structure 
    // you can make a convenient form of a graph
    map_graph_t map_graph;
    build_map_graph_structure(graph, map_graph);
    nIters = 1; // only for reference implementation 
    // ---------------------------------
    // end of code fragment
    //----------------------------------
    clock_gettime(CLOCK, &finish_ts_build);
    build_time = (finish_ts_build.tv_nsec - (double)start_ts_build.tv_nsec) * 1.0e-9 + (finish_ts_build.tv_sec - (double)start_ts_build.tv_sec);

    // find communities
    vector<vertex_id_t> component_id(graph.n);
    struct timespec start_ts, finish_ts;

    perf_t *perf = (perf_t *)malloc(nIters * sizeof(perf_t));

    for (int i = 0; i < nIters; ++i) {
   	cout << "start " << i << " iter..." << flush;
    	component_id.assign(graph.n, 0);
        clock_gettime(CLOCK, &start_ts);
        // ---------------------------------------------------
        // you can change only the code of the next function
	    community_detection(graph.n, map_graph, component_id);
     	// ---------------------------------------------------
        clock_gettime(CLOCK, &finish_ts);
        perf[i].time = (finish_ts.tv_nsec - (double)start_ts.tv_nsec) * 1.0e-9 + (finish_ts.tv_sec - (double)start_ts.tv_sec);
        perf[i].modularity = modularity(graph, component_id);
   	    cout << "finished. Time is " << perf[i].time << " secs. Modularity is " << perf[i].modularity << endl;
    }

    perf_t avg_perf;
    avg_perf.time = avg_perf.modularity = 0;
    for (int i = 0; i < nIters; ++i) {
    	avg_perf.time += (perf[i].time + build_time);
    	avg_perf.modularity += perf[i].modularity;
    }
    
    avg_perf.time /= nIters;
    avg_perf.modularity /= nIters;
    cout.precision(2);
    cout << filename << ":";
    cout << "    nIters: " << nIters;
    cout << "    build_time: " << fixed << build_time << " seconds";
    cout << "    avg_time: " << fixed << avg_perf.time << " seconds";
    cout.precision(5);
    cout << "    modularity: " << avg_perf.modularity;
    
    modularity_reference = (modularity_reference > 0) ? modularity_reference : avg_perf.modularity; 
    cout << "    mod_ref: " << modularity_reference;

    double mod_diff = avg_perf.modularity - modularity_reference;
    double rating_coef = mod_diff > 0 ? (avg_perf.modularity / modularity_reference - 1)*10 : avg_perf.modularity / modularity_reference;
    double rating = 10000 * rating_coef / avg_perf.time;
    cout << "    RATING: " << fixed << rating << endl;
    
    return 0;
}

