/*
 * \file Vertices.c
 * \brief: implementation of the Vertices class, derived from DataSet class
 */

/*Headers: {{{1*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>

#include "./DataSet.h"
#include "../shared/shared.h"
#include "../include/include.h"
#include "../EnumDefinitions/EnumDefinitions.h"

using namespace std;
/*}}}*/

/*Object constructors and destructor*/
/*FUNCTION Vertices::Vertices(){{{1*/
Vertices::Vertices(){
	enum_type=VerticesEnum;
	return;
}
/*}}}*/
/*FUNCTION Vertices::~Vertices(){{{1*/
Vertices::~Vertices(){
	return;
}
/*}}}*/

/*Numerics management*/
/*FUNCTION Vertices::DistributeDofs{{{1*/
void  Vertices::DistributeDofs(int numberofobjects,int numberofdofsperobject){

	extern int num_procs;
	extern int my_rank;

	int  i;
	int  dofcount=0;
	int* alldofcount=NULL;
	int* truedofs=NULL;
	int* alltruedofs=NULL;

	/*Go through objects, and distribute dofs locally, from 0 to numberofdofsperobject*/
	for (i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		vertex->DistributeDofs(&dofcount);
	}

	/* Now every object has distributed dofs, but locally, and with a dof count starting from 
	 * 0. This means the dofs between all the cpus are not unique. We now offset the dofs of eache
	 * cpus by the total last dofs of the previus cpu, starting from 0.
	 * First: bet number of dofs for each cpu*/
	alldofcount=(int*)xmalloc(num_procs*sizeof(int));
	#ifdef _HAVE_MPI_
	MPI_Gather(&dofcount,1,MPI_INT,alldofcount,1,MPI_INT,0,MPI_COMM_WORLD);
	MPI_Bcast(alldofcount,num_procs,MPI_INT,0,MPI_COMM_WORLD);
	#else
	alldofcount[0]=dofcount;
	#endif

	/* Every cpu should start its own dof count at the end of the dofcount from cpu-1*/
	dofcount=0;
	if(my_rank!=0){
		for(i=0;i<my_rank;i++){
			dofcount+=alldofcount[i];
		}
	}
	for (i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		vertex->OffsetDofs(dofcount);
	}

	/* Finally, remember that cpus may have skipped some objects, because they were clones. For every 
	 * object that is not a clone, tell them to show their dofs, so that later on, they can get picked 
	 * up by their clones: */
	truedofs   =(int*)xcalloc(numberofobjects*numberofdofsperobject,sizeof(int));
	alltruedofs=(int*)xcalloc(numberofobjects*numberofdofsperobject,sizeof(int));
	for (i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		vertex->ShowTrueDofs(truedofs);
	}
	#ifdef _HAVE_MPI_
	MPI_Allreduce((void*)truedofs,(void*)alltruedofs,numberofobjects*numberofdofsperobject,MPI_INT,MPI_MAX,MPI_COMM_WORLD);
	#else
	for(i=0;i<numberofobjects*numberofdofsperobject;i++)alltruedofs[i]=truedofs[i];
	#endif

	/* Now every cpu knows the true dofs of everyone else that is not a clone*/
	for (i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		vertex->UpdateCloneDofs(alltruedofs);
	}

	/* Free ressources: */
	xfree((void**)&alldofcount);
	xfree((void**)&truedofs);
	xfree((void**)&alltruedofs);
}
/*}}}*/
/*FUNCTION Vertices::FlagClones{{{1*/
void  Vertices::FlagClones(int numberofobjects){

	int i;
	extern int num_procs;

	int* ranks=NULL;
	int* minranks=NULL;

	/*Allocate ranks: */
	ranks=(int*)xmalloc(numberofobjects*sizeof(int));
	minranks=(int*)xmalloc(numberofobjects*sizeof(int));

	for(i=0;i<numberofobjects;i++)ranks[i]=num_procs; //no cpu can have rank num_procs. This is the maximum limit.

	/*Now go through all our objects and ask them to report to who they belong (which rank): */
	Ranks(ranks);

	/*We need to take the minimum rank for each vertex, and every cpu needs to get that result. That way, 
	 * when we start building the dof list for all vertexs, a cpu can check whether its vertex already has been 
	 * dealt with by another cpu. We take the minimum because we are going to manage dof assignment in increasing 
	 * order of cpu rank. This is also why we initialized this array to num_procs.*/
	#ifdef _HAVE_MPI_
	MPI_Allreduce ( (void*)ranks,(void*)minranks,numberofobjects,MPI_INT,MPI_MIN,MPI_COMM_WORLD);
	#else
	for(i=0;i<numberofobjects;i++)minranks[i]=ranks[i];
	#endif

	/*Now go through all objects, and use minranks to flag which objects are cloned: */
	for(i=0;i<this->Size();i++){
		/*For this object, decide whether it is a clone: */
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		vertex->SetClone(minranks);
	}

	/*Free ressources: */
	xfree((void**)&ranks); 
	xfree((void**)&minranks);

}
/*}}}*/
/*FUNCTION Vertices::NumberOfVertices{{{1*/
int Vertices::NumberOfVertices(void){

	int i,sid;
	int max_sid=0;
	int vertex_max_sid;

	for(i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		sid=vertex->Sid();
		if (sid>max_sid)max_sid=sid;
	}

	#ifdef _HAVE_MPI_
	MPI_Reduce (&max_sid,&vertex_max_sid,1,MPI_INT,MPI_MAX,0,MPI_COMM_WORLD );
	MPI_Bcast(&vertex_max_sid,1,MPI_INT,0,MPI_COMM_WORLD);
	max_sid=vertex_max_sid;
	#endif

	/*sid starts at 0*/
	max_sid++;

	/*return:*/
	return max_sid;
}
/*}}}*/
/*FUNCTION Vertices::Ranks{{{1*/
void   Vertices::Ranks(int* ranks){

	/*Go through a dataset, and for each object, report it cpu: */

	int i;
	int rank;
	int sid;
	
	for(i=0;i<this->Size();i++){
		Vertex* vertex=(Vertex*)this->GetObjectByOffset(i);
		rank=vertex->MyRank();
		sid=vertex->Sid();
		
		/*Plug rank into ranks, according to id: */
		ranks[sid]=rank; 
	}
	return;
}
/*}}}*/
