/***************************************************************************
                          locus.cpp  -  Librairie d'objets permettant de manipuler des données
                          						spécifiques aux locus
                             -------------------
    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.                                   *
 *                                                                         *
 ***************************************************************************/

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

//typedef biolib::vecteurs::ChaineCar ChaineCar;

// quelques fonctions mathématiques...

long double fact (int n) {
	long double res (n);
	long double i;

	if (n == 0) return (1);

	for (i = n-1; i > 1 ; i--) {
	//	res = res * i;
		res *= i;
	}

	return (res);
}

long double fact (int n, int m) {
	long double res (1);
	int i;
///equivalent a: fact (n) / fact (m)
	if (n == m) return(1);
	if (n > m) {
		res = n;
		for (i = n-1; i > m ; i--) {
		//	res = res * i;
			res *= i;
		}
		return (res);
	}
	else {
		res = m;
		for (i = m-1; i > n ; i--) {
			res *= i;
		}
		return (1/res);
	}
}
long double comb (int n, int m) {
	long double res;
	int a(m),b(n-m), swap;

	if (b > a) {
		swap = a;
		a = b;
		b = swap;
	}
	res = fact (n,a);
//	res = fact (n) / fact (m);
	res = res / fact (b);

	return (res);
}

/*
//constructeur
Individu::Individu (int nball) {

	_nom.assign("");
	_Ppop = 0;
	_nball = nball;
//	_tabPall = new (Allele*[nball]);
	_tabPall.resize(nball);
//	for (i=0;i < nball;i++) _tabPall.push_back(0);
}
*/

//constructeur de copies
Locus::Locus(const Locus& original, Jeupop * Pjeu) {

	unsigned long i;
	unsigned long nball(original.get_nball());

	_nom = original._nom;
	_Pjeu = Pjeu;

	for (i=0;i < nball;i++) {
		_tabPall.push_back(new Allele(*original._tabPall[i],this));
//		_tabPall[i]->set_Plocus(this);
	}
}

/*//constructeur de copies
Locus::Locus(const Locus& original) {

	long i;
	long nball(original.get_nball());

	_nom = original._nom;
//	_Pjeu = original._Pjeu;

	for (i=0;i < nball;i++) {
		_tabPall.push_back(new Allele(this, original.get_nomall(i)));
//		if (original.getPall(i)->_nul) nul = true;
	}

}
*/

//constructeur
Locus::Locus (long nball) {
	long i;

	_nom.assign("");
	_Pjeu = 0;

	for (i=0;i < nball;i++) _tabPall.push_back(new Allele(this));
}

Locus::Locus (Jeupop * pjeu, long nball) {
	long i;

	_Pjeu = pjeu;

	_nom.assign("");

	for (i=0;i < nball;i++) _tabPall.push_back(new Allele(this));
}

//constructeur

//destructeur
Locus::~Locus () {
//cerr << "Locus::~Locus debut" << endl;
	int i;
	int nball(get_nball());

	for (i=0;i < nball;i++) delete (_tabPall[i]);

//cerr << "Locus::~Locus fin" << endl;
//	delete [] _tabPall;
}



void Locus::reset(Jeupop * Pjeu, long nballnew) {
	long i;
	long nball(get_nball());

	for (i=0;i < nball;i++) delete (_tabPall[i]);

//	delete [] _tabPall;

	_Pjeu = Pjeu;
//	_nball = nball;
//	_tabPall = new (Allele*[nball]);
	_tabPall.resize(nballnew);

	for (i=0;i < nballnew;i++) {
		_tabPall[i] = new Allele(this);
	}

}

//constructeur
/*Population::Population (int nbind) {
	int i;

	_nom.assign("");
	_Pjeu = 0;
	_nbind = nbind;
//	_tabPind = new (Individu*[nbind]);

	_nbloc = 0;
	_nploidie = 0;
//	_tabPall = new (Allele*[0]);

	for (i=0;i < _nbind;i++) _tabPind.push_back(new(Individu));
}*/



/*void Population::sort_all() {
	//tri de _tabPall par loci
	Allele * Pallswap;
	int nbcopies(_nploidie * _nbind);
	int i, j, pos;

	for (i=0;i < _nbloc; i++) {

		for (j=1; j<nbcopies; j++) {
			if (_tabPall[(i * nbcopies) + j] < _tabPall[(i * nbcopies) + j-1]) {
				//swap
				pos = j;
				while((_tabPall[(i * nbcopies) + pos] < _tabPall[(i * nbcopies) + pos-1]) && (pos>0)) {
					Pallswap = _tabPall[(i * nbcopies) + pos-1];
					_tabPall[(i * nbcopies) + pos-1] = _tabPall[(i * nbcopies) + pos];
					_tabPall[(i * nbcopies) + pos] = Pallswap;
					pos--;
				}
			}
		}
	}
}*/


Allele * Locus::getPallNul() {
	// donne un pointeur sur l'allele nul
	Allele* res;
	unsigned long i;

	res = 0;
	for (i=0; i < _tabPall.size(); i++) {
		if(_tabPall[i]->_miss) {
			res = _tabPall[i];
			break;
		}
	}

	if (res == 0) { //on crée l'allèle nul
		_tabPall.push_back(new Allele(this, "00"));
		res = _tabPall.back();
	}

	return (res);
}

Allele* Locus::getPall(const string & nom) const{
	// donne un pointeur sur l'allele du nom 'nom'
	Allele* res;
	unsigned long i;

	res = 0;
//cerr << "getPall dans Locus debut " << nom << endl;
	for (i=0; i < _tabPall.size(); i++) {
//cerr << "coucou " << _tabPall[i]->_nom << endl;
		if(_tabPall[i]->_nom == nom) {
			res = _tabPall[i];
			break;
		}
	}
//cerr << "getPall dans Locus fin " << res << endl;

	return (res);
}


bool Locus::f_verifnum(int typedenum) const {
	// Vérification du type de numérotation des allèles
//	1 => type Genepop stricte (2digits)
//	2 => nom d'alleles = numéros
//	3 => nom d'alleles = numéros < 999
		//Anomalie 2-> les numéros d'allèles format genepop ne sont pas présents

	long nball(get_nball());
	long i;

	for (i=0; i < nball; i++) {
		if (!(_tabPall[i]->f_verifnum(typedenum))) return(false);
	}

	return(true);
}


void Locus::ifAjouterAllele(const Allele * Pall) {
	//ajout d'un allele dans un locus
	// => recherche un allele preexistant
	// vérifié le 11/09/2000
	unsigned long i, taille(get_nball());
	long nball(-1);

	for (i = 0 ; i < taille; i++) {
		if (*Pall == *getPall(i)) {
			nball = i;
			break;
		}
	}
	if (nball >= 0) { //l'allele existe deja
		return;
	}
	else { //il faut le creer
		_tabPall.push_back(new Allele(*Pall, this));
	}
}

const Locus& Locus::operator= (const Locus & rval) {
	long i;
	long nball(rval.get_nball());

	reset(_Pjeu, nball);
	_nom = rval._nom;

	for (i=0;i < nball;i++) {
		*(_tabPall[i]) = *(rval._tabPall[i]);
	//	_tabPall[i]->set_Plocus(this);
	}

	return(*this);
}

void Locus::f_trad2Gpop(ostream& sortie) {
//transformation des noms d'alleles en chiffres < a 100
	long i;
	long nball(get_nball());
//	char mot[5];
	biolib::vecteurs::ChaineCar mot;

	sortie << endl;
	sortie << "Locus: " << _nom << endl;

	for (i=0; i < nball; i++) {
		mot.assign("");
		
		if (_tabPall[i]->_miss) {
			sortie << _tabPall[i]->_nom << " <--> ";
			_tabPall[i]->set_nom("00");
			sortie << _tabPall[i]->_nom << endl;
		}
		else {
			sortie << _tabPall[i]->_nom << " <--> ";
			
	//		itoa(i+1,mot,10);
	//		mot[2] = '\0';
			if ((i+1) < 10) {
	//			mot[1] = mot[0];
	//			mot[0] = '0';
				mot += '0';
				mot.AjEntier(i+1);
			}
			else if ((i+1) < 100) {
				mot.AjEntier(i+1);
			}
			else cerr <<"erreur..."<< endl;
			_tabPall[i]->set_nom(mot);

			sortie << _tabPall[i]->_nom << endl;
		}
	}
}



void Locus::set_PJeupop(Jeupop * Pjeu) {
	_Pjeu = Pjeu;
}

unsigned long Locus::get_numloc() {
	return(_Pjeu->get_numloc(this));
}
/** Enlève les allèles non représentés dans Jeupop
 */
void Locus::f_nettoieAlleles(){
	unsigned long i;

	for (i = 0; i < _tabPall.size(); ) {
//cerr << i << " " << _tabPall.size() << endl;
    if (_Pjeu->r_allelenonpresent(_tabPall[i])) {
			delete _tabPall[i];
			_tabPall.erase(_tabPall.begin()+i);
		}
		else i++;
	}
}

unsigned long Locus::get_nballnonnuls() const{
/** Retourne le nombre d'allèles non nuls pour ce locus */
	unsigned long nball(0);
	unsigned long i;
	
	for (i=0; i < _tabPall.size(); i++) if (_tabPall[i]->r_nonnul()) nball++;

	return (nball);
}


void Locus::set_microsat_correction(long double valmin, long double valmax, unsigned int value) {
 // corrige la taille des allèles microsats dans l'intervalle valmin<>valmax avec la valeur value

 // 1) trouver les allèles compris entre valmin et valmax
 vector<long double> tab_tailles;
 biolib::vecteurs::ChaineCar nom;
 unsigned int i;

 tab_tailles.resize(_tabPall.size());
 for (i=0; i < _tabPall.size(); i++) {
    tab_tailles[i] = (long double) atof(_tabPall[i]->get_nom().c_str());
 }

 for (i=0; i < tab_tailles.size(); i++) {
    if ((tab_tailles[i]>=valmin) && (tab_tailles[i]<=valmax)) {
       _tabPall[i]->set_nbrepet(value);
       nom = "C";
       nom.AjEntier(value);
       _tabPall[i]->set_nom(nom);       
    }
 }
 
 
}

Allele * Locus::new_allele(const string & name, unsigned int nbrepeat) {
  Allele * Pallele(new Allele(this,name));
  //Pallele->set_nom(name);
  Pallele->set_nbrepet(nbrepeat);
  _tabPall.push_back(Pallele);
  return(Pallele);
}
