
/***************************************************************************
                          strucpop.cpp  -  Librairie d'objets permettant de manipuler des données
                          						spécifiques aux StrucPops
                             -------------------
    begin                : ven sep 01 10:25:55 CEST 2000
    copyright            : (C) 2000 by Olivier Langella CNRS UPR9034
    email                : Olivier.Langella@pge.cnrs-gif.fr
 ***************************************************************************/

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


#include"locus.h"
#include"jeupop.h"
#include "strucpop.h"

StrucPop::StrucPop (Jeupop * pdonnees) {
//cerr << "StrucPop::StrucPop (Jeupop * pdonnees) constructeur debut" << endl;

	_nom.assign("_");
	_Pjeu = pdonnees;
	_nbind = 0;

	_nploidie = _Pjeu->get_nploidie();
	_nbloc = _Pjeu->get_nbloc();
//cerr << "StrucPop::StrucPop (Jeupop * pdonnees) constructeur fin" << endl;

}


StrucPop::StrucPop(const StrucPop & rval,MetaPop * Pmetapop, Jeupop * Pjeu) {
	// constructeur de copies
	// vérifié le 11/09/2000
	unsigned long i;

//cerr << "StrucPop(const StrucPop & rval,MetaPop * Pmetapop, Jeupop * Pjeu) début" << endl;
	//if (popori.get_nploidie() != _nploidie) throw Anomalie(2);
	// => effacer l'ancien contenu
	reset();

	_nom = rval._nom;
	_Pjeu = Pjeu;
	_Pmetapop = Pmetapop;
	_nbloc = rval.get_nbloc();
	_nploidie = rval.get_nploidie();

	_tabAllIntrogressant.resize(rval._tabAllIntrogressant.size());

	for (i=0; i < _tabAllIntrogressant.size(); i++) {
		_tabAllIntrogressant[i] = get_Pall(rval._tabAllIntrogressant[i]->get_NomLocus(),rval._tabAllIntrogressant[i]->get_Nom());
	}
//cerr << "StrucPop(const StrucPop & rval,MetaPop * Pmetapop, Jeupop * Pjeu) fin" << endl;

}

StrucPop::~StrucPop () {
}

void StrucPop::reset() {
	// remise à zéro
	unsigned long i;

	_tabAllIntrogressant.resize(0);
	_VcalcInd.resize(0);

	for (i=0; i < _tabPind.size();i++) delete _tabPind[i];

	_tabPind.resize(0);
	_nbind = 0;
	_nbloc = 0;
	
}


void StrucPop::r_alldiff(int numlocus, Vecteur<Allele *> &tabPall) const {
//num locus, tableau sur les Alleles differents de ce locus
	unsigned int i, j;
	unsigned int nbind(get_nbind());
	
	tabPall.resize(0);
	tabPall.push_back(_tabPind[0]->_tabPall[numlocus * _nploidie]);
	for (i=0;i < nbind;i++) {
		for (j=0;j < _nploidie;j++) {
			if (tabPall.Existe(_tabPind[i]->_tabPall[(numlocus * _nploidie) + j])) {}
			else {
				tabPall.push_back(_tabPind[i]->_tabPall[(numlocus * _nploidie) + j]);
			}	
		}
	}

//	return(tabPall.size());
}

unsigned long StrucPop::r_nballnonnulsBootInd(unsigned long locus) const {
	// retourne le nb de copies d'allèles non nuls pour un locus
	//spécifique aux calculs avec bootstrap sur les individus
	unsigned long resultat(0);
	unsigned long nbind(_VcalcInd.size());
	unsigned long nploidie(get_nploidie());
	unsigned long i, j;
	
	for (i=0;i < nbind;i++) {
		for (j=0;j < nploidie;j++) {
			if (_VcalcInd[i]->_tabPall[(locus * nploidie) + j]->_miss) continue;
			resultat++;
		}
	}

	return(resultat);
}

void StrucPop::f_bootstrap() {
	//bootstrap sur les individus
	unsigned long nbind(_tabPind.size());
	unsigned long j, tirage;

	if (_VcalcInd.size() != nbind) _VcalcInd.resize(nbind);

	// tirage des individus
	for (j = 0; j < nbind; j ++ ) {
		tirage = (unsigned long) (nbind * ((long double)rand() / ((long double) RAND_MAX)));
		_VcalcInd[j] = _tabPind[tirage];
	}
}

double StrucPop::r_rare(unsigned long nreduit, unsigned int numlocus) const{
	//nouvelle diversité allèlique pour un échantillon
	// réduit: nreduit, pour un locus
//	cerr << "coucou";
//	cerr << nreduit << " " << numlocus << " " << get_nbind() << endl;
	int i;
	long double resultat(0);
	long double inter;
//	long double inter2;
	int nploidie(get_nploidie());
	Vecteur<Allele *> tabPallDiff;

	//taille de l'échantillon allèlique
	unsigned long nechall(get_nbind() * nploidie);
	nreduit *= nploidie;
	if (nreduit > nechall) return(resultat);

	//nombre d'allèles différents
	r_alldiff(numlocus, tabPallDiff);
//	cerr << "coucou";
	int nball(tabPallDiff.size());

//cerr << nball  << endl;
	// ne compte pas les allèles nuls (manquants)
	for (i=0;i<nball;i++) {
		if (tabPallDiff[i]->_miss) {
			nechall -= (unsigned long) r_nbcopall(tabPallDiff[i]);
			tabPallDiff.Suppr(i);
			nball--;
			if (nreduit > nechall) return(resultat);
			break;
		}
	}
//cerr << nball  << endl;
//cerr << endl;

//	if (nreduit == nechall) return(nball);

	//nombre de copies d'un allèle
	unsigned long ncopies;

	for (i=0;i<nball;i++) {
		ncopies = (unsigned long) r_nbcopall(tabPallDiff[i]);
/* 		inter = comb ((nechall - ncopies)  , nreduit);
		inter2 = comb(nechall , nreduit);
		inter = inter / inter2;*/

		if (nreduit < ncopies) {
			inter = fact((int) (nechall - ncopies), (int) ((nechall - ncopies) - nreduit)) * fact((int) (nechall - nreduit) , (int) nechall);
		}
		else {
			inter = fact((int) (nechall - nreduit), (int) ((nechall - ncopies) - nreduit)) * fact((int) (nechall - ncopies) , (int) nechall);
		}
		resultat = resultat + (1 - inter);
//cerr << nechall << " " << nreduit << endl;
//cerr << inter2 << endl;
//cerr << inter << endl;
//cerr << inter2 << endl;
//cerr << resultat << endl;
//cin >> ncopies;  
	}

	return((double) resultat);
}


void StrucPop::ifAjouterIndividu(const Individu * Pind) {

}


Allele* StrucPop::get_Pall(unsigned long ind, unsigned long locus, unsigned int numall) const {
	unsigned long index;
	unsigned int nploidie(get_nploidie());

	if (locus >= get_nbloc()) throw Anomalie(1);
	if (numall >= nploidie) throw Anomalie(1);

	index = (locus * nploidie) + numall;
	return(_tabPind[ind]->_tabPall[index]);
}


Allele * StrucPop::get_Pall(const string & nomlocus, const string & nomall) const {
	//recherche l'allele correspondant dans son propre jeu de pop:
	/// n'utiliser que lors d'une copie
	Allele * res;
	Locus * lelocus;

	lelocus = _Pjeu->get_Plocus(nomlocus);
	if (lelocus==0) throw Anomalie (5);

	res = 0;
//cerr << "get_Pall debut " << lelocus << " " << lelocus->get_nom() << " " << nomall << endl;
	res = lelocus->getPall(nomall);
//cerr << "get_Pall allele fin" << res << " " << nomlocus << " " << nomall << endl;

	if (res==0) throw Anomalie (6);

	return(res);
}


void StrucPop::resize_loc() {
	// allocation de mémoire pour un changement de nb de locus
	/// ATTENTION, ne marche que pour agrandir _tabPall !!!
	unsigned long i, j, k;
	vector<Allele*> tabPallnuls;

	if (_nbloc == _Pjeu->get_nbloc()) return;

	if (_Pjeu->get_nploidie() != _nploidie) throw Anomalie(2);

	_nbind = _tabPind.size();
//	_nploidie = _Pjeu->get_nploidie();

	_nbloc = _Pjeu->get_nbloc();


	tabPallnuls.resize(_nbloc);
	for (j = 0; j < _nbloc; j++) {
		tabPallnuls[j] = _Pjeu->get_Plocus(j)->getPallNul();
	}

	for (i=0;i < _nbind;i++) {
		//on remplit les nouveaux alleles des individus
		// par des alleles nuls

		for (j = 0; j < _nbloc; j++) {
			for (k = 0; k < _nploidie; k++) {
				if (((j * _nploidie) + k) >= _tabPind[i]->_tabPall.size()) {

					_tabPind[i]->_tabPall.push_back(tabPallnuls[j]);
				}
			}			
		}
	}

}

inline Allele* StrucPop::get_Pall(unsigned long ind,unsigned long all) const {
	return(_tabPind[ind]->_tabPall[all]);
}

const string & StrucPop::get_nomind(long nbind) const {
	// donne le nom de l'individu nbind
//cerr << "StrucPop::get_nomind(long nbind) " << nbind << endl;
	return (_tabPind[nbind]->get_nom());
}

void StrucPop::AjouterAllIntrogressant(Allele * Pall) {
	if (_tabAllIntrogressant.Existe(Pall) == false) {
//cerr << "StrucPop::AjouterAllIntrogressant" << endl;
		_tabAllIntrogressant.push_back(Pall);
	}
}

long double StrucPop::f_heterozygotieatt(unsigned long locus, unsigned long * Pnbpopcalc) const {
//Calcul de l'heterozygotie attendue pour un locus
// Hs expected heterozygosity
// 1- somme des frequences au carre de chaque allele
// ok verifie le 11/10/2000

	long unsigned i,nballloc,allnonnuls;
	long double sfreqcarre, freq;//, Hs;
	Allele * Pall;

	nballloc = _Pjeu->get_Plocus(locus)->get_nball();
	allnonnuls = r_nballnonnuls(locus);
	if (allnonnuls == 0) {
		*Pnbpopcalc -= 1;
		return (0); // a verifier
	}

	for (sfreqcarre=0,i=0; i < nballloc; i++) {
		Pall = _Pjeu->get_Pall(locus,i);
		if (Pall->_miss) continue;
		freq = ((long double) r_nbcopall(Pall)) / ((long double) allnonnuls);
		sfreqcarre += (freq * freq);			
//cerr <<"StrucPop::f_heterozygotieatt freq" <<  freq << endl;
	}		
	//Hs += (long double) 1 - sfreqcarre;
//cerr <<"StrucPop::f_heterozygotieatt Hs" << ((long double) 1) - sfreqcarre << endl;
	return (((long double) 1) - sfreqcarre);	
}

long double StrucPop::f_heterozygotieobs(unsigned long locus, unsigned long * Pnbpopcalc) const {
//Calcul de l'heterozygotie observée pour un locus
//frequence des hétérozygotes dans cette population à ce locus
// verifie le 11/10/2000 "génétique et Evolution" Solignac

	long unsigned i, nbindcalc, nbind(get_nbind()),nbhetero;

	for (nbindcalc=1, i=0, nbhetero = 0; i < nbind; i++,nbindcalc++) {
		try {
			if (_tabPind[i]->r_esthetero(locus)) nbhetero++;
		}
		catch (Individu::Anomalie pb) {
 			switch (pb.le_pb) {
			case 13:
				//cerr << _("tt ERROR in Individu object: ") << pb.le_pb << endl;
				nbindcalc--;				
				break;
			default:
				cerr << _("ERROR in Individu object: ") << pb.le_pb << endl;
				break;
			}
		}
		
	}
	nbindcalc--;
//	cerr << "StrucPop::f_heterozygotieobs" << endl;
//	cerr << locus << " nbindcalc : " << nbindcalc << " nbhetero : " << nbhetero << endl;
	
  if (nbindcalc == 0) {
		*Pnbpopcalc -= 1;
		return(0);
	}
//cerr <<"StrucPop::f_heterozygotieobs Hi" << ((long double) nbhetero) / ((long double) nbind) << endl;
//cerr <<"StrucPop::f_heterozygotieobs nbhetero" <<  nbhetero << " nbind" << nbind << endl;
	return (((long double) nbhetero) / ((long double) nbindcalc));	
}

long double StrucPop::f_calcfreq(Allele * Pall) const {
	//calcul de la fréquence de l'allèle Pall dans cette population
	long double nballnonnuls(r_nballnonnuls(_Pjeu->get_numloc(Pall->get_Ploc())));

//	r_nballnonnuls(_Pjeu->get_numloc(Pall->get_Ploc());

	return(((long double) r_nbcopall(Pall)) / nballnonnuls);
}


void StrucPop::set_nom(const string & nom) {
//	int pos;

	_nom.assign(nom);

/*	pos = _nom.find(" ", 0);
	while (pos != -1) {
		_nom.replace(pos, _nom.size(), "_");
		pos = _nom.find(" ", 0);
	}
  */
}

/*
const StrucPop& StrucPop::operator= (const StrucPop & rval) {
	long i;
	long nbind(rval.get_nbind());

//cerr << "operator = pop, debut " << nbind << " " << rval._nom << endl;
	if (get_nploidie() != rval.get_nploidie()) throw Anomalie(2);
	if (get_nbloc() != rval.get_nbloc()) throw Anomalie(3);

	reset(_Pjeu, nbind);

	_nom = rval._nom;

	try {
		for (i=0; i < nbind; i++) {
//cerr << "operator = pop, individu " << endl;
			*(_tabPind[i]) = *(rval._tabPind[i]);
		}
	}
	catch (Individu::Anomalie pb) {
		cerr << "erreur de type: " << pb.le_pb << " dans l'operateur = de Population" << endl;
	}
//cerr << "operator = pop, coucou2 " << endl;

	_tabAllIntrogressant.resize(rval._tabAllIntrogressant.size());

	for (i=0; i < rval._tabAllIntrogressant.size(); i++) {
		_tabAllIntrogressant[i] = get_Pall(rval._tabAllIntrogressant[i]->get_NomLocus(),rval._tabAllIntrogressant[i]->get_Nom());
	}
//cerr << "operator = pop, fin " << endl;

	return(*this);
}

*/
 

/** Supprime l'individu Pind du tableau */
void StrucPop::SupprtabIndividu(Individu * Pind){
//cerr << "StrucPop::SupprtabIndividu debut" << endl;
	unsigned long i;
	for (i = 0 ; i < _tabPind.size(); i++) {
		if (_tabPind[i] == Pind) {
//cerr << "StrucPop::SupprtabIndividu" << Pind->get_nom() << endl;
			_tabPind.erase(_tabPind.begin() + i);
			_nbind--;
		}
	}
	if (get_niveau() !=0) _Pmetapop->SupprtabIndividu(Pind);
//cerr << "StrucPop::SupprtabIndividu fin" << endl;
}

long StrucPop::f_calcfreqabsolue(Allele * Pall) const {
	//calcul de la fréquence absolue de l'allèle Pall dans cette population

	return(r_nbcopall(Pall));
}
/** réaffectation du nombre de locus */
void StrucPop::set_nbloc(){
	_nbloc = _Pjeu->get_nbloc();
}


