void buildAnalyticalTheta2(
			MoleculeCoordinates *soluteCoors,
			MoleculeCharges *soluteCharges,
			MoleculeCharges *solventCharges,
			SiteMultiplicities *siteMultiplicities,
			TabFunction  ***phiIndex,
			TabFunction  ***phiLJIndex,  // NULL if no LJ correction
			SigmaEpsilonTable *sigmaEpsilonTable,
			Real **chiMultipliers,
			Solution3DRISMR *theta			// out
		   )
{
// \Theta_{\gamma} = \sum_s{ q_s * \sum_{\alpha} multiplicity[alpha] * chiMultipliers[alpha][gamma] * phi[alpha][gamma](r_s)  }

Integer numSoluteSites = soluteCoors->getNumSites();
Integer numSolventSites = siteMultiplicities->getNumSites();


Integer *multiplicities = siteMultiplicities->getSiteMultiplicities();
Real *q_solute = soluteCharges->getCharge();
Real *q_solvent = solventCharges->getCharge();

ThetaSiteSitePotentialWithLJ thetaPotential	(
				numSoluteSites,		//Integer NsoluteSites,
				numSolventSites,	//Integer NsolventSites,
				q_solute,		//Real *q_solute,
				q_solvent,		//Real *q_solvent,
				multiplicities,		//Real *siteMultipliers,
				chiMultipliers,		//Real *chiMultipliers,
				phiIndex,		//TabFunction ***phiIndex
				phiLJIndex,		//TabFunction ***phiLJIndex
				sigmaEpsilonTable	//SigmaEpsilonTable *sigmaEpsilonTable
				);


#if USE_buildPotential
buildPotential (	// no averaging

	soluteCoors,		//MoleculeCoordinates *soluteCoors,
	&thetaPotential,	//SiteSitePotential *siteSitePotential,
	theta			//Solution3DRISMR *potential // output
);

#endif

#if USE_buildPotential2 || USE_buildPotential4



buildPotential(	//averaging
	soluteCoors,		//MoleculeCoordinates *soluteCoors,
	&thetaPotential,	//SiteSitePotential *siteSitePotential,
	theta			//Solution3DRISMR *potential // output
);

#endif

}
Potential* marginalizeNotRequired(Graph* moral, Potential* source) {

	Potential* potential = NULL;
	Potential* marginalized = NULL;
	Variable* var = NULL;

	int i;

	for (i=0;i<source->nvars;i++) {
		var = source->vars[i];
		if (!getVertexById(&moral,var->id)) {
			// Inicializa a variável auxiliar
			if (potential==NULL) potential = buildPotential (source->id, source->nvars, source->vars, source->values);

			// Marginalização
			marginalized = marginalizePotential(var,potential);

			// Destrói o potencial temporário
			destroyPotential(&potential);

			potential = marginalized;
			marginalized = NULL;
		}
	}
	// Libera memória
	var = NULL;

	return potential;
}
void executeElimination(Elimination** refElimination) {

	Elimination* elimination = *refElimination;	
	SingleLinkedListNode* current;

	Potential* marginalized = NULL;
	Potential* result = NULL;
	Potential* A;
	Potential* B;

	current = elimination->potentials->first;
	if (slllength(elimination->potentials)>1) {

		// Executa o produto dos potenciais 2 a 2

		// Executa o primeiro produto
		A = (Potential*)current->data;
		current = current->next;
		B = (Potential*)current->data;
		result = multPotentials(A,B);

#ifdef DUMPELM
	fprintf(DBGOUT,"\tResult:\n");
	dumpPotential(result);
#endif

		// Executa os demais produtos
		current = current->next;
		while (current!=NULL) {
			A = result;
			result = multPotentials(A,(Potential*) current->data);

#ifdef DUMPELM
	fprintf(DBGOUT,"\tResult:\n");
	dumpPotential(result);
#endif

			destroyPotential(&A);
			current = current->next;
		}

		// Faz a marginalização
		if (elimination->marginalize==1) {
			// Faz a marginalização
			marginalized = marginalizePotentialMPI(elimination->variable,result);

			// Cria ou atualiza o result da eliminação
			if (elimination->result==NULL) {
				// Cria o novo potencial
				elimination->result = buildPotential(-1,marginalized->nvars,marginalized->vars,marginalized->values);
			} else { 
				// Apenas copia os novos valores
				memcpy(elimination->result->values,marginalized->values,marginalized->nvalues*sizeof(double));
			}

		} else {
			// Cria ou atualiza o result da eliminação
			if (elimination->result==NULL) {
				// Cria o novo potencial
				elimination->result = buildPotential(-1,result->nvars,result->vars,result->values);
			} else { 
				// Apenas copia os novos valores
				memcpy(elimination->result->values,result->values,result->nvalues*sizeof(double));
			}
		}


	} else {
		// Faz a marginalização
		if (elimination->marginalize==1) {
			marginalized = marginalizePotentialMPI(elimination->variable,(Potential*)current->data);

			// Cria ou atualiza o result da eliminação
			if (elimination->result==NULL) {
				// Cria o novo potencial
				elimination->result = buildPotential(-1,marginalized->nvars,marginalized->vars,marginalized->values);
			} else { 
				// Apenas copia os novos valores
				memcpy(elimination->result->values,marginalized->values,marginalized->nvalues*sizeof(double));
			}

		} else {
			// Cria ou atualiza o result da eliminação
			if (elimination->result==NULL) {
				// Cria o novo potencial
				elimination->result = buildPotential(-1,((Potential*)current->data)->nvars,((Potential*)current->data)->vars,((Potential*)current->data)->values);
			} else { 
				// Apenas copia os novos valores
				memcpy(elimination->result->values,((Potential*)current->data)->values,((Potential*)current->data)->nvalues*sizeof(double));
			}
		}
	}

	// Desloca final
	if (marginalized!=NULL) destroyPotential(&marginalized);

	// Desaloca o result
	if (result!=NULL) destroyPotential(&result);

	// Para garantir...
	current = NULL;
}
Potential* doBNLinearVariableElimination(int nxq, int* xq, int nxe, int* xe, Potential** findings, BayesNet* bayesnet, int* elmorder, Graph* moral) {

	Variable* first = NULL;		// Primeira variável a ser eliminada do potencial

	VertexNode* current = NULL;		// Variável auxiliar para percorrer o Moral Graph

	Potential* result = NULL;		// Potencial resultante da sequencia de operações
	Potential* potential = NULL;	// Variável potencial auxiliar

	Elimination** eliminations = NULL;	// Array com operções de eliminações

	int varelmorder[bnsize(bayesnet)];	// Mapa o id da variável para a ordem de eliminação
	int used[bnsize(bayesnet)];			// Vetor de flag para indicar se um potencial foi utilizado

	SingleLinkedListNode* sllcurrent;

	int i,j,id;
	int nvars, ct_used;

	// Número de variáveis
	nvars = graphsize(moral);

	// Aloca o espaço necessário para o array de operações
	eliminations = malloc(nvars*sizeof(Elimination*));

	for (i=0;i<nvars;i++) {
		// Inicializa o mapa
		varelmorder[elmorder[i]] = i;

		// Inicializa a flag
		used[elmorder[i]] = 0;

		// Monta as eliminações e popula varelmorder
		eliminations[i] = (Elimination*) buildElimination(i,bayesnet->variables[elmorder[i]],1);
	}

	// Tomando Xq, se não NULL, marca as operações que não devem ser marginalizadas
	if (xq!=NULL) { // Apenas por segurança
		for (i=0;i<nxq;i++) eliminations[varelmorder[xq[i]]]->marginalize = 0;
	}

	for (i=0, ct_used=0;i<nvars && ct_used<nvars;i++) {

		// Atribui os potenciais da operação de eliminação
		// 1. Verifica o nó da variável "da vez"
		if (!used[elmorder[i]]) {
			id = elmorder[i];
			if (findings && findings[id]) {
				// Marginaliza a distribuição de probabilidade se necessário
				potential =(bayesnet->potentials[id]->nvars>1)?marginalizeNotRequired(moral,bayesnet->potentials[id]):NULL;
				// Produto do finding pela distribuição de probabilidade
				sllappend(&eliminations[i]->potentials,multPotentials((potential?potential:bayesnet->potentials[id]),findings[id]));
				// Libera o espaço alocado (se necessário)
				if (potential) destroyPotential(&potential);
			} else {
				sllappend(&eliminations[i]->potentials,bayesnet->potentials[id]);
			}
			used[id] = 1;
			ct_used++;
		}

		// 2. Verifica os filhos da variável "da vez"
		current = getVertexChildren(getVertexById(&bayesnet->graph,elmorder[i]))->first;
		while (current!=NULL) {
			if (getVertexById(&moral,current->vertex->id) && !used[current->vertex->id]) {
				id = current->vertex->id;
				if (findings && findings[id]) {
					// Marginaliza a distribuição de probabilidade se necessário
					potential =(bayesnet->potentials[id]->nvars>1)?marginalizeNotRequired(moral,bayesnet->potentials[id]):NULL;
					// Produto do finding pela distribuição de probabilidade
					sllappend(&eliminations[i]->potentials,multPotentials((potential?potential:bayesnet->potentials[id]),findings[id]));
					// Libera o espaço alocado (se necessário)
					if (potential) destroyPotential(&potential);
				} else {
					sllappend(&eliminations[i]->potentials,bayesnet->potentials[id]);
				}
				used[id] = 1;
				ct_used++;
			}
			current = current->next;
		}
	}

	// Executa linearmente a eliminação de variáveis
	for (i=0;i<nvars;i++) {

		// Executa a eliminação
		executeElimination(&eliminations[i]);

		// Adiciona o resultado a uma operação futura
		if (i<(nvars-1)) {
			// Identifica em cada potencial a primeira variável a ser eliminada
			first = NULL;
			potential = eliminations[i]->result;
			for (j=0;j<potential->nvars;j++) {
				if (varelmorder[potential->vars[j]->id]>i) {
					if (first==NULL || varelmorder[potential->vars[j]->id] < varelmorder[first->id]) first = potential->vars[j];
				}
			}
			// Adiciona o potential à operação de eliminação correspondente à próxima variável a ser eliminada
			if (first!=NULL) sllappend(&eliminations[varelmorder[first->id]]->potentials,potential);
		}
	}

	// Copia o resultado da última eliminação
	result = buildPotential(-1,eliminations[(nvars-1)]->result->nvars,eliminations[(nvars-1)]->result->vars,eliminations[(nvars-1)]->result->values);

	// Normaliza o resultado final se necessário
	if (result!=NULL) normalize(result->nvalues,&result->values);

	// Desaloca a memória alocada para esta iteração preservando as demais estruturas

	// Elimina as operações executadas
	for (i=0;i<nvars;i++) destroyElimination(&eliminations[i]);
	free(eliminations);
	eliminations = NULL;

	return result;
}
// Potential* doBNVariableElimination(Potential** findings, ThreadTree* threadtree) {
Potential* doBNVariableElimination(ThreadTree* threadtree, MPI_Comm  *everyone){

// COMECO DO TESTE
		ThreadVertexNode* current;
		ThreadVertex* vertex = NULL;
		current = threadtree->vertices->first;
		while (current != NULL) {
			// Se necessario determina o root
			if (threadtree->root==NULL && current->vertex->parent==NULL) threadtree->root = current->vertex;
			vertex = current->vertex;
			Elimination** elim = (Elimination **)((vertex)->data);
			send_Vertex(&vertex,everyone,vertex->id,vertex->id);
			current = current->next;
		}

		if (threadtree->root==NULL) {
			fprintf(stderr,"NO ROOT !");
			exit(1);
		}
		printf("Todos os vertex enviados por mpi\n");
		
		Elimination* f = NULL;
		receive_Elimination (&f,everyone,MPI_ANY_SOURCE);
		printf("Elimination final recebido\n");
		
		if (f->result!=NULL) normalize((f->result)->nvalues,&(f->result)->values);		
		dumpPotential(f->result);
		
		//return f->result;
// FIM TESTE
	


	ThreadVertex* thread = NULL;	// Variável auxiliar que instancia um vértive da árvore de threads

	Potential* result = NULL;		// Potencial resultante da sequencia de operações

	Elimination** eliminations = NULL;	// Array com operções de eliminações

	int i,nvars;


	// Número de variáveis
	nvars = sizeThreadTree(threadtree);

#ifdef NOTHREADS
	// Executa as threats uma a uma
	for (i=0;i<nvars;i++) {
		thread = getThreadVertex(&threadtree,i);
		thread->status = THREAD_READY;
		run(&thread);
		join(&thread);
	}
#else
	// Executa a árvore como um todo para fazer a inferência
	// execute(&threadtree);
	restart(&threadtree,everyone);
	joinThreadTree(&threadtree);

#endif

	// Recupera os dados de execução das threads
	eliminations = (Elimination**) getThreadVertex(&threadtree,0)->data;

	// Copia o resultado da última eliminação
	result = buildPotential(-1,eliminations[(nvars-1)]->result->nvars,eliminations[(nvars-1)]->result->vars,eliminations[(nvars-1)]->result->values);

	// Normaliza o resultado final se necessário
	if (result!=NULL) normalize(result->nvalues,&result->values);

	return result;
	
}