#include <stdio.h>
#include <iostream>
#include <vector>
#include <map>
#include <fstream>
#include <time.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <iomanip>
#include <list>
#include <stdlib.h>
#include <ctime>
#include "T_matrix.cpp"

#define MAX_RAND 32767

using namespace	std;

ifstream inn;
ofstream off;
ofstream off_graph;
//ofstream off_all;

T_matrix<short int> maticeC("matice C");// - dodelat naplneni
T_matrix<short int> maticeT("matice T");// dodelat naplneni
T_matrix<short int> maticeB("matice B");
T_matrix<short int> p_maticeB("pom. matice B");

int sirkaT, sirkaC, vyskaT, vyskaC;
time_t start_time, end_time;

T_array<int> CROW_COUNTER("CROW_COUNTER");
T_array<int> TROW_COUNTER("TROW_COUNTER");

T_matrix<long int> matches_statistics("statistika matches");
T_matrix<long int> rank_statistics("statistika rank");

char test_name[50];
char output_graph[50], output_out[50];
char output_dir[50];


/** ****************************** Logging ********************************* */

/** Vypis
- tamplaty a procedury k vypisu matic */
template<class InputIterator> void vypis(ostream &os, InputIterator zacatek,
         InputIterator konec)
{
  for(InputIterator i = zacatek; i != konec; i++)  {
    os << setw(2) << *i << " ";
  }
  os << endl;
}

void vypis_ns(ostream &os, vector<char> pole) {
  int i, j;
  for (i = 0; i < pole.size(); i++)
    os << pole[i];
}

template<class InputIterator> void vypisBinary(ostream &os, InputIterator zacatek, InputIterator konec) {
  for(InputIterator i = zacatek; i != konec; i++)
    if (*i == 0 || *i == 1) os << *i;
    else os << "-";
}

template<class AnyType> void vypisBinary(ostream &os, AnyType value) {
  if (value == 0 || value == 1) os << value;
  else os << "-";
}


void vypisCas(ostream &os, long int cas) {
  os << floor(cas / 3600) << ":" << right;
  os << setfill('0') << setw(2) <<  floor((cas % 3600) / 60) << ":";
  os << setfill('0') << setw(2) << (cas % 60) << endl;
  os << setfill(' ');
}

/** SortDoubleDesc
- pomocna procedura k sestupnemu setrizeni vektoru double */
bool SortDoubleDesc(const double &a, const double &b) {
  return a > b;
}

/** VypisOddelovac
- vypise oddelovac do prislusneho streamu */
void VypisOddelovac(ostream &os) {
  os << "-------------------------------------------------------------------------------" << endl;
}

/** VypisStatistiku
- vypise do prislusneho streamu statistiky */
void VypisStatistiku(ostream &os) {
  // spocitame statistiku
  vector<double> matches_variance(sirkaT);
  vector<double> rank_variance(sirkaT);

  for (int colT=0; colT<sirkaT; colT++) {
    long int max_hits = 0;
    long int tot_hits = 0;
    long int hits;

    // vypocet variace matches pro sloupce T
    for (int colC=0; colC<sirkaC; colC++) {
      hits = matches_statistics[colT][colC];
      if (hits > max_hits) {
        max_hits = hits;
      }
      tot_hits += hits;
    }
    if (max_hits != 0) 
      matches_variance[colT] = (double) max_hits / (double) tot_hits * (double) sirkaC;
    else
      matches_variance[colT] = 0;

    max_hits = 0;
    tot_hits = 0;
    // vypocet variace matches pro sloupce T
    for (int r=0; r<sirkaT; r++) {
      hits = rank_statistics[colT][r];
      if (hits > max_hits) {
        max_hits = hits;
      }
      tot_hits += hits;
    }
    if (max_hits != 0) 
      rank_variance[colT] = (double) max_hits / (double) tot_hits * (double) sirkaT;
    else
      rank_variance[colT] = 0;

  }

  os << "Variace pole matches pro sloupce T: " << endl;
  vypis(os, matches_variance.begin(), matches_variance.end());
  os << "Variace pole rank pro sloupce T: " << endl;
  vypis(os, rank_variance.begin(), rank_variance.end());

  os << "Matice vyberu kombinace T->C" << endl;
  matches_statistics.vypis(os);
  os << "Matice vyberu kombinace T->rank" << endl;
  rank_statistics.vypis(os);

}

double GetRandomInterval() {
   long int r = rand();
   double ret = (double) r / RAND_MAX;
   return ret;
}

/** ************************************ prace s listem pro tvoreni permutaci *******************************/

void InitList(list<int> &l, int size) {
  l.clear();
  for (int i = 0; i < size; i++) {
    l.push_back(i);
  }
}

void RemoveValue(list<int> &l, int value) {
  list<int>::iterator it;

  it = l.begin();
  while ( *it < value && it != l.end() ) it++;
  if (*it == value) l.erase(it);
}

int RemoveRandom(list<int> &l) {
  list<int>::iterator it;

  int r = rand() % l.size();
  int ret;

  // nahodne vybereme hodnotu
  it = l.begin();
  for (int j = 0; j < r; j++) it++;
  ret = *it;
  l.erase(it);

  return ret;
}

/** ****************************** Row Assignment ********************************* */

/** MakeRowAssignment
- pro globalni matici B zjisti, jestli je mozne provest prirazeni vsech radku C k radkum T
  a tim overi, jestli aktualni matice B muze byt resenim
- vraci true, pokud je prirazeni mozne, jinak false
- ve vystupnim vektoru X jsou dvojice radek T -> radek C, ma smysl pouze pri vraceni true
*/
bool MakeRowAssignment(vector<int> &X) {
  int c_crow, c_trow, min, crow, trow;

  X.resize(vyskaT);

  // vynulovani poli s poctem jednicek v sloupcich a radcich matice B (radkyB, sloupceB)
  CROW_COUNTER.SetConstValue(0);
  TROW_COUNTER.SetConstValue(0);

  // spocitam pocet jednicek ve sloupcich a radcich
  for (c_crow = 0; c_crow < vyskaC; c_crow++)
    for (c_trow = 0; c_trow < vyskaT; c_trow++)
      if (maticeB[c_crow][c_trow] == 1) {
          CROW_COUNTER[c_crow]++;
          TROW_COUNTER[c_trow]++;
       }

  // prirazeni provedu pro kazdy sloupec maticeB (coz reprezentuje radky matice T)
  for (int i = 0; i < vyskaT; i++) {

    min = vyskaC + 1;
    // vyber sloupce v matici B, ktery ma nejmene jednicek, tj. trow (radek T do matche)
    for (c_trow = 0; c_trow < vyskaT; c_trow++)
      if (TROW_COUNTER[c_trow] != -1 && TROW_COUNTER[c_trow] < min) {
        min = TROW_COUNTER[c_trow];
        trow = c_trow;
        }

    // nenasli jsme ke sloupci matice B zadny radek B, tzn. nenasli jsme k radku T radek C
    if (min == 0) return false;

    min = vyskaT + 1;
    // vyber radku matice B, ktery ma nejmene jednicek, tj. crow (radek C do matche)
    for (c_crow = 0; c_crow < vyskaC; c_crow++)
      if (CROW_COUNTER[c_crow] != -1 && maticeB[c_crow][trow] == 1 && CROW_COUNTER[c_crow] < min) {
        min = CROW_COUNTER[c_crow];
        crow = c_crow;
      }

    // zapamatuji match		
    X[trow] = crow;

    // projedu sloupec maticeB trow a dekrementuju counter CROW tam, kde existuje prirazeni = 1
    for (c_crow = 0; c_crow < vyskaC; c_crow++) 
      if (maticeB[c_crow][trow] == 1 && CROW_COUNTER[c_crow] != -1)
        CROW_COUNTER[c_crow]--;

    // projedu radek maticeB crow a dekrementuju counter TROW tam, kde existuje prirazeni = 1
    for (c_trow = 0; c_trow < vyskaT; c_trow++) 
      if (maticeB[crow][c_trow] == 1 && TROW_COUNTER[c_trow] != -1)
        TROW_COUNTER[c_trow]--;

    // odeberu radek a sloupec z matice B
    CROW_COUNTER[crow] = -1;
    TROW_COUNTER[trow] = -1;

  }

  // povedlo se najit prirazeni
  return true;
}

/** ********************************* Jedinec ******************************** */
class Jedinec {
private:
  // zakodovani
  vector<int> matches;		// prirazeni sloupcu matice C k sloupcum matice TP
  vector<int> NM;		// priznak negovani sloupcu matice TP
  vector<int> rank;		// poradi prirazeni sloupcu matice TP - index = poradi, hodnota = sloupec T

  // fitness a kvalita jedince
  double fitness;		// hodnota fitness (kvalita) jedince
  double loss;
  int columnsT_matched;		// pocet nalezenych sparovani
  int direct_matches;
  int direct_neg_matches;

  // informativni a pomocne
  vector<int> X;		// prirazeni radku matice C k radkum matice T - vypocitava se spolu s fitnes
  bool crossover_flag;		// flag indikujici, ze jedinec byl krizen

  vector<double> columnsTP_loss;

public:

/** Jedinec()
- implicitni konstruktor - vytvori prazdneho jedince (pole rank je prazdne) */
  Jedinec(bool init_chromozome = true);

/** Jedinec(const Jedinec& puvodni)
- kopirovaci konstruktor */
  Jedinec(const Jedinec& puvodni);

/** CommonGenes(const Jedinec& puvodni)
- zjisti, jestli jedinci maji spolecne geny, aby mezi sebou souperili pri selekci */
  int CommonGenes(const Jedinec& s);

/** Vypis(ostream &os)
- vypise detaily o jedinci */
  void Vypis(ostream &os);

/** VypisMatches(ostream &os)
- vypise pole matches v trochu citelnem formatu T->C */
  void VypisMatches(ostream &os) const;

/** VypisRank(ostream &os)
- vypise pole rank */
  void VypisRank(ostream &os);

/** VypisFlags(ostream &os)
- vypise vsechny flagy za sebou */
  void VypisFlags(ostream &os);

/** Check() 
- zkontroluje jedince (pole rank, matches, row_assignement) */
  void Check();

/** operator<(const Jedinec &druhy) 
- operator pro porovnavani jedincu */
  bool operator<(const Jedinec &druhy);

/** operator>(const Jedinec &druhy) 
- operator pro porovnavani jedincu */
  bool operator>(const Jedinec &druhy);

/** Get metody jedince */
  double GetFitness() const;
  int GetColumnsTMatched() const;
  int GetDirectMatches() const;
  int GetDirectNegMatches() const;
  double GetLoss() const;
  bool GetCrossoverFlag() const;
  int GetRank(int r) const;

/** Set metody jedince */
  void SetCrossoverFlag(bool value);
  void SetCM(int r, int colTP, int colC, int neg);

/** Metody pro vypocet fitness */

/** Jedinec::CountFitness(bool use_discarding)
- spocita fitnes jedince, upravi pole rank */
  void CountFitness(bool use_discarding);

/** Geneticke operatory */

/** bool DetectConflict(int colT)
- zkontroluje jestli je mozne pridat do pole rank sloupec colT */
  bool DetectConflict(int colT);

/** static Jedinec UniformCrossover(Jedinec &prvni, Jedinec &druhy);
- provede uniformni krizeni dvou jedincu -> vzniknou potomek1, potomek */
  static Jedinec UniformCrossover(Jedinec &prvni, Jedinec &druhy);

/** static Jedinec OnePointCrossover(Jedinec &rodic1, Jedinec &rodic2);
- provede 1-bodove krizeni dvou jedincu -> vzniknou potomek1, potomek */
  static Jedinec OnePointCrossover(Jedinec &rodic1, Jedinec &rodic2);

/** void MatchesMutation(double pm);
- provede mutaci pole matches */
  void MatchesMutation(double pm);

/** void RankInsertMutation(double pr1);
- provede mutaci pole rank - vlozeni v permutaci */
  void RankInsertMutation(double pr1);

/** void RankMoveMutation(double pr2);
- provede mutaci pole rank - vymena sousedu */
  void RankMoveMutation(double pr2);

/** Jedinec::AdjustStatistics()
- upravi statistiky pro pole rank a matches */
  void AdjustStatistics();

/** Jedinec::CreatePLAFile()
- vytvori vystupni PLA soubor z tohoto jedince */
  void CreatePLAFile();

/** Jedinec::CreateMatchesFile()
- vytvori vystupni soubor matches z tohoto jedince */
  void CreateMatchesFile();

/** Jedinec::CheckRank() 
- zkontroluje pole rank jedince, jestli v nem nejsou duplicitni sloupce T */
  bool CheckRank();

/** Jedinec::CheckMatches() 
- zkontroluje pole matches jedince, jestli v nem nejsou sloupce C zpusobujici kolizi */
  bool CheckMatches();

/** Jedinec::CheckRowAssignment()
- zkontroluje prirazeni radek ve vektoru X - jestli prorazane matice C odpovida sparovanym sloupcum a matici T */
  bool CheckRowAssignment();

};

/** ************************************ Populace - definice ************************************* */
//TODO dodelat komentare

class Populace {
public:
  int size;
  int generation;
  vector<Jedinec> vect;

  Jedinec best_jedinec;
  double best_fitness;
  int best_columnsT_matched;

  double avg_fitness;
  double avg_cm;

  vector<int> genes_amount;

  Populace(int size);

  Populace(const Populace& s);

  void Evaluate(bool use_discarding);

  void CountProperties();

  void Sort();

  void GenerateNext(double ps, int crossover_method, double pc, 
                    double pm, double pr1, double pr2, bool use_thresholding = true);

  int SelectIndividual(bool use_threshold);

  void ZeroAvgValues();

  void Vypis(ostream &os);

  void GraphLog(ostream &os);

  Jedinec Get(int i) const;

};


/** ************************************* Jedinec ************************************ */

/** Jedinec::Jedinec
- implicitni konstruktor - nahodne naplni pole matches, rank a NM + ostatni atributy vynuluje */
  Jedinec::Jedinec(bool init_chromozome)
    :fitness(-1), columnsT_matched(-1), direct_matches(-1), direct_neg_matches(-1),
    crossover_flag(0), loss(0)
  {
    rank.resize(sirkaT);
    matches.resize(sirkaT);
    NM.resize(sirkaT);

    if (!init_chromozome) {
      for (int i = 0; i < sirkaT; i++) {
        matches[i] = -1;
        NM[i] = -1;
        rank[i] = -1;
      }

    } else {

      for (int colT = 0; colT < sirkaT; colT++) {
        matches[colT] = rand() % sirkaC;
        NM[colT] = rand() % 2;
      }

      // nahodne naplneni pole rank - nesmi obsahovat dve stejne hodnoty sloupce T
      list<int> columnsT;
      InitList(columnsT, sirkaT);
      for (int r = 0; r < sirkaT; r++) {
        rank[r] = RemoveRandom(columnsT);
      }
    }
  }

/** Jedinec::Jedinec(const Jedinec& puvodni)
- kopirovaci konstruktor */
  Jedinec::Jedinec(const Jedinec& puvodni): 
    matches(puvodni.matches), X(puvodni.X),
    NM(puvodni.NM), rank(puvodni.rank),
    fitness(puvodni.fitness), columnsT_matched(puvodni.columnsT_matched), direct_matches(puvodni.direct_matches),
    direct_neg_matches(puvodni.direct_neg_matches), loss(puvodni.loss),
    crossover_flag(puvodni.crossover_flag) {}

/** Jedinec::CommonGenes(const Jedinec& puvodni)
- zjisti, jestli jedinci maji alespon polovinu spolecnych genu, aby mezi sebou souperili pri selekci */
  int Jedinec::CommonGenes(const Jedinec& s) {
    int cnt = 0;

    for (int r1 = 0; r1 < columnsT_matched; r1++)
      for (int r2 = 0; r2 < s.columnsT_matched; r2++)
        if ( rank[r1] == rank[r2] ) cnt++;

    return cnt;

  }

/** Jedinec::Vypis(ostream &os)
- vypise detaily o jedinci */
void Jedinec::Vypis(ostream &os) {
    os << "Vypis kodovani jedince" << endl;
    os << "F, CM, DM, DNM, P: " << fitness << ", " << columnsT_matched << ", " << direct_matches << ", " << direct_neg_matches << ", " << loss << endl;
    os << "Flagy: ";
    VypisFlags(os);
    os << endl;
    os << "Matches: ";
    VypisMatches(os);
    os << "Rank: ";
    VypisRank(os);
    os << "X: ";
    vypis(os, X.begin(), X.end());
    os << endl;
  }

/** Jedinec::VypisMatches(ostream &os)
- vypise pole matches v trochu citelnem formatu T->C */
  void Jedinec::VypisMatches(ostream &os) const {
    int colT, colC, r;
    for (r = 0; r < columnsT_matched; r++) {
      colT = rank[r];
      colC = matches[colT];
      os << setw(2) << colT << "->" << setw(2) << colC << "(" << NM[colT] << ") ";
    }
    os << endl;
  }

/** Jedinec::VypisRank(ostream &os)
- vypise pole rank */
  void Jedinec::VypisRank(ostream &os) {
    vypis(os, rank.begin(), rank.end());
  }

/** Jedinec::VypisFlags(ostream &os)
- vypise vsechny flagy pekne za sebou */
  void Jedinec::VypisFlags(ostream &os) {
    os << this->crossover_flag;
  }

/** Jedinec::Check() 
*/
  void Jedinec::Check() {

    if (!CheckRowAssignment()) {
      cout << "Kontrola prirazeni radek jedince neprobehla v poradku, chyba algoritmu!!!!" << endl;
      exit(2);
    }

    if (!CheckRank()) {
       cout << "Kontrola pole rank jedince neprobehla v poradku, chyba algoritmu!!!!" << endl;
       Vypis(cout);
       exit(2);
    }

    if (!CheckMatches()) {
       cout << "Kontrola pole matches jedince neprobehla v poradku, chyba algoritmu!!!!" << endl;
       Vypis(cout);
       exit(2);
    }
  }


/** Jedinec::CheckRank() 
- zkontroluje pole rank jedince, jestli v nem nejsou duplicitni sloupce T */
  bool Jedinec::CheckRank() {

    for (int i=0; i<sirkaT; i++) {
      if (rank[i] >= sirkaT || rank[i] < 0)
        return false;
      for (int j=0; j<sirkaT; j++)
        if (i != j && rank[i] == rank[j])
          return false;
    }
    return true;
  }

/** Jedinec::CheckMatches() 
- zkontroluje pole matches jedince, jestli v nem nejsou kolizni sloupce T */
  bool Jedinec::CheckMatches() {

    for (int colT = 0; colT < sirkaT; colT++)
      if ( matches[colT] < 0 || matches[colT] >= sirkaC )
        return false;

    return true;
  }

/** Jedinec::operator<(const Jedinec &druhy) 
- operator pro porovnavani jedincu */
  bool Jedinec::operator<(const Jedinec &druhy) {
    return fitness < druhy.fitness;
  }

/** Jedinec::operator>(const Jedinec &druhy) 
- operator pro porovnavani jedincu */
  bool Jedinec::operator>(const Jedinec &druhy) {
  return fitness > druhy.fitness;
  }

/** Get metody Jedince */
  double Jedinec::GetFitness() const {
    return fitness;
  }
  int Jedinec::GetColumnsTMatched() const {
    return columnsT_matched;
  }
  int Jedinec::GetDirectMatches() const {
    return direct_matches;
  }
  int Jedinec::GetDirectNegMatches() const {
    return direct_neg_matches;
  }
  double Jedinec::GetLoss() const {
    return loss;
  }
  bool Jedinec::GetCrossoverFlag() const {
    return crossover_flag;
  }
  int Jedinec::GetRank(int r) const {
    return rank[r];
  }


/** Set metody jedince */
  void Jedinec::SetCrossoverFlag(bool value) {
    crossover_flag = value;
  }
  void Jedinec::SetCM(int r, int colTP, int colC, int neg) {
    rank[r] = colTP;
    matches[colTP] = colC;
    NM[colTP] = neg;
  }


/** Jedinec::CountFitness(bool use_discarding)
- spocita fitnes jedince, upravi pole rank */
void Jedinec::CountFitness(bool use_discarding) {

    int rowC, rowT, level;
    int colC, colT, neg;
    bool assigned;
    long int remaining_ones, cols_discarded;
    vector<int> p_X;

    p_X.resize(vyskaT);

    // vsechna pole matice B nastavime na 1
    maticeB.SetConstValue(1);

    columnsT_matched = 0;
    direct_matches = 0;
    direct_neg_matches = 0;
    level = 0;
    cols_discarded = 0;

    do {

      if (use_discarding) {
        // vytvoreni kopie matice B
        p_maticeB.copy(maticeB);
      }

      // vybrani matche
      colT = rank[level];	// dalsi sloupec T
      colC = matches[colT];	// k nemu matchujici sloupec C
      neg = NM[colT];		// k nemu priznak o negovani

      // uprava matice B - pri uprave matice B si spocitame pomer mezi poctem 1 a poctem nastavenych 1->0
      for (rowC = 0; rowC < vyskaC; rowC++)
        for (rowT = 0; rowT < vyskaT; rowT++) {
          if (maticeB[rowC][rowT] == 1) 
          {
          if (((neg == 1) && (maticeT[rowT][colT] != 2) && (maticeC[rowC][colC] == maticeT[rowT][colT])) ||
            ((neg == 0) && (maticeT[rowT][colT] != 2) && (maticeC[rowC][colC] != maticeT[rowT][colT]))) {
              maticeB[rowC][rowT] = 0;
            }
          }
          }
//cout << "CF5: " << level << ", colTP: " << colTP << ", colC: " << colC << ", neg: ";
//cout << neg << ", CHO: " << changed_ones << endl;

      if (assigned = MakeRowAssignment(p_X)) {
        // povedlo se prirazeni, zapamatujeme X a budem zkouset matchovat dalsi sloupec T

        X = p_X;
        level++;

        // pokud jde o direct match inkrementujeme direct_matches, resp. direct_neg_matches direct_matches
        if ( colT == colC && neg == 0 ) direct_matches++;
        if ( colT == colC && neg == 1 ) direct_neg_matches++;

      } else {
        // nepovedlo se prirazeni

        if (use_discarding) {
          // soucasny sloupec T jedince, na kterem prirazeni nevyslo zaradime nakonec (zahodime)
          for (int i = level; i < sirkaT - 1; i++) {
            rank[i] = rank[i + 1];
          }
          rank[sirkaT - 1] = colT;

          matches[colT] = rand() % sirkaC;
          NM[colT] = rand() % 2;

          // inkrementujeme cislo urcujici pocet vymenenych sloupcu
          cols_discarded++;

          // obnovime puvodni matici B ze zalohy
          maticeB.copy(p_maticeB);

        }

      }

    } while ( (assigned && level < sirkaT) || (use_discarding && level < sirkaT - cols_discarded) );

    // spocitam pocet jednicek v matici B
    remaining_ones = 0;
    for (rowC = 0; rowC < vyskaC; rowC++)
      for (rowT = 0; rowT < vyskaT; rowT++)
        if (maticeB[rowC][rowT] == 1) remaining_ones++;

    loss = (double) remaining_ones / (double) (vyskaC * vyskaT);
    columnsT_matched = level;
    //fitness = (double) columnsT_matched + loss;
    fitness = (double) columnsT_matched + (double) (direct_matches + direct_neg_matches) / sirkaT;
}


/** Jedinec::DetectConflict(int colT)
- zkontroluje jestli je mozne pridat do pole rank sloupec colT */
bool Jedinec::DetectConflict(int colT) {
  int r = 0;
  while (rank[r] != -1 && r < sirkaT) {
    if (rank[r] == colT) return true;
    r++;
  }
  return false;
}

/** Jedinec Jedinec::UniformCrossover(Jedinec &prvni, Jedinec &druhy);
- provede uniformni krizeni dvou jedincu -> vzniknou potomek1, potomek */
Jedinec Jedinec::UniformCrossover(Jedinec &prvni, Jedinec &druhy) {
  int colT, colC, neg, r, counter;
  list<int>::iterator it_basketT, it_basketC, it_basketNM;
  list<int> basketT, basketC, basketNM, columnsT;

  Jedinec potomek(false);

//  potomek.Vypis(cout);

  InitList(columnsT, sirkaT);

  // do kose dame pary prvniho rodice
  for (r = 0; r<prvni.columnsT_matched; r++) {
    colT = prvni.rank[r];
    basketT.push_back(colT);
    basketC.push_back(prvni.matches[colT]);
    basketNM.push_back(prvni.NM[colT]);
  }

  // do kose dame pary prvniho rodice
  for (r = 0; r<druhy.columnsT_matched; r++) {
    colT = druhy.rank[r];
    basketT.push_back(colT);
    basketC.push_back(druhy.matches[colT]);
    basketNM.push_back(druhy.NM[colT]);
  }

  // dokud neni kos prazdny, tak z nej vybirame pary
  counter = 0;
  while (basketT.size() > 0 && counter < sirkaT) {
    // vybereme nahodny par
    r = rand() % basketT.size();

    // zjistime hodnoty nahodneho paru
    it_basketT = basketT.begin();
    it_basketC = basketC.begin();
    it_basketNM = basketNM.begin();
    for (int i = 0; i < r; i++) {
      it_basketT++; it_basketC++; it_basketNM++;
    }
    colT = *it_basketT;
    colC = *it_basketC;
    neg = *it_basketNM;

    // zkotrolujeme, jestli tento par muzeme do potomka pridat
    if ( !potomek.DetectConflict(colT) ) {

      // neni konflikt - muzeme par pridat
      potomek.SetCM(counter, colT, colC, neg);
      counter++;

      // odebereme sloupce C a TP ze zatim nepouzitych sloupcu
      RemoveValue(columnsT, colT);
    }
    // vyhodime par z kose
    basketT.erase(it_basketT);
    basketC.erase(it_basketC);
    basketNM.erase(it_basketNM);
  }

  // doplnime ostatni sloupce ze zbyvajicich v T
  for (int i = counter; i < sirkaT; i++) {
    // nahodne vybereme sloupec T - rovnou ho i odebereme z listu
    colT = RemoveRandom(columnsT);

    // sloupec C priznak negovani je zcela nahodny
    colC = rand() % sirkaC;
    neg = rand() % 2;

    // pridame par do potomka
    potomek.SetCM(i, colT, colC, neg);
  }
  return potomek;
}

/** Jedinec Jedinec::OnePointCrossover(Jedinec &rodic1, Jedinec &rodic2);
- provede 1-bodove krizeni dvou jedincu -> vzniknou potomek1, potomek */
Jedinec Jedinec::OnePointCrossover(Jedinec &rodic1, Jedinec &rodic2) {
  int r, colT, colC, neg, counter;
  list<int> columnsT;

  Jedinec potomek(false);
  InitList(columnsT, sirkaT);

//  cout << "OC1: " << endl;
  int c = rand() % rodic1.columnsT_matched;
  for (r = 0; r < c; r++) {
    colT = rodic1.rank[r];
    colC = rodic1.matches[colT];
    neg = rodic1.NM[colT];

    potomek.SetCM(r, colT, colC, neg);
    RemoveValue(columnsT, colT);
  }

//  cout << "OC2: " << endl;
  counter = c;
  for (r = 0; r < rodic2.columnsT_matched; r++) {
    colT = rodic2.rank[r];

//    cout << "OC2a: " << endl;
    if (!potomek.DetectConflict(colT)) {
//      cout << "OC2b: " << endl;
      colC = rodic2.matches[colT];
      neg = rodic2.NM[colT];

      potomek.SetCM(counter, colT, colC, neg);
      counter++;

//      cout << "OC2c: " << endl;
      RemoveValue(columnsT, colT);
//      cout << "OC2d: " << endl;
    }
  }

//  cout << "OC3: " << endl;
  // doplnime ostatni sloupce ze zbyvajicich v T
  for (int i = counter; i < sirkaT; i++) {
    // nahodne vybereme sloupec T - rovnou ho i odebereme z listu
    colT = RemoveRandom(columnsT);

    // sloupec C priznak negovani je zcela nahodny
    colC = rand() % sirkaC;
    neg = rand() % 2;

    // pridame par do potomka
    potomek.SetCM(i, colT, colC, neg);
  }
//  potomek.Vypis(cout);
//  cout << "OC4: " << endl;

  return potomek;

}

/** void MatchesMutation(double pm);
- provede mutaci pole matches */
void Jedinec::MatchesMutation(double pm) {
  for (int colT = 0; colT < sirkaT; colT++) {
    if (GetRandomInterval() < pm) {
      matches[colT] = rand() % sirkaC;
      NM[colT] = rand() % 2;
    }
  }
}

/** void RankInsertMutation(double pr1);
- provede mutaci pole rank - vlozeni v permutaci */
void Jedinec::RankInsertMutation(double pr1) {
  int tmp, pos;
//  cout << "RIM" << endl;
//  VypisRank(cout);
  for (int r = 0; r < sirkaT; r++) {
    if (GetRandomInterval() < pr1) {
      pos = rand() % sirkaT;
//      cout << "r: " << r << "pos: " << pos << endl;
      if (pos > r) {
        tmp = rank[r];
        for (int i = r; i < pos; i++) rank[i] = rank[i+1];
        rank[pos] = tmp;
      } else if (pos < r) {
        tmp = rank[r];
        for (int i = r; i > pos; i--) rank[i] = rank[i-1];
        rank[pos] = tmp;
      }
    }
  }
//  VypisRank(cout);
}

/** void RankMoveMutation(double pr2);
- provede mutaci pole rank - vymena sousedu */
void Jedinec::RankMoveMutation(double pr2) {
  int tmp;
  for (int r = 0; r < sirkaT - 1; r++) {
    if (GetRandomInterval() < pr2) {
      tmp = rank[r];
      rank[r] = rank[r+1];
      rank[r+1] = tmp;
    }
  }
}

/** Jedinec::AdjustStatistics()
- upravi statistiky pro pole rank a matches */
void Jedinec::AdjustStatistics() {
  int colT, colC, r;

  for (r=0; r<rank.size(); r++) {
    colT = rank[r];
    colC = matches[colT];
    rank_statistics[colT][r]++;
    matches_statistics[colT][colC]++;
  }
}

/** Jedinec::CheckRowAssignment()
- zkontroluje prirazeni radek ve vektoru X - jestli prorazane matice C odpovida sparovanym sloupcum a matici T */
bool Jedinec::CheckRowAssignment() {
  int r, colT, colC, rowC, rowT;
  short int neg;

  if (X.size() > 0) {
    for (r = 0; r < GetColumnsTMatched(); r++) {
      colT = rank[r];
      colC = matches[colT];
      neg = NM[colT];
      for (rowT = 0; rowT < vyskaT; rowT++) {
        rowC = X[rowT];
        if (maticeT[rowT][colT] != 2)
        if ((neg == 1 && maticeC[rowC][colC] == maticeT[rowT][colT]) ||
            (neg == 0 && maticeC[rowC][colC] != maticeT[rowT][colT]))
          return false;
      }
    }
  }
  return true;
}


/** Jedinec::CreatePLAFile()
- vytvori vystupni PLA soubor z tohoto jedince */
void Jedinec::CreatePLAFile() {
  int r, cnt, rowC, rowT, colT;
  vector<int> not_matched_columns;
  short int value;
  char output_pla[50];
  ofstream os;

  sprintf(output_pla, "%s%s.pla", output_dir, test_name);

  os.open(output_pla);

  os << ".i " << sirkaC << endl;
  os << ".o " << sirkaT - columnsT_matched << endl;
  os << ".p " << vyskaT << endl;
  os << ".type fr" << endl;

  // vytvorime si serazene pole nesparovanych sloupcu
  not_matched_columns.resize(0);
  cnt = 0;
  for (r = columnsT_matched; r < sirkaT; r++) {
    not_matched_columns.push_back(rank[r]);
    cnt++;
  }
  sort(not_matched_columns.begin(), not_matched_columns.end());

  // po radcich matice T vypisujeme
  for (rowT = 0; rowT < vyskaT; rowT++) {
    rowC = X[rowT];
    maticeC.vypisRadek_bin(os, rowC, false);
    //vypisBinaryMatrixRow(os, maticeC[rowC], sirkaC);
    os << " ";

    for (r = 0; r < cnt; r++) {
      colT = not_matched_columns[r];
      vypisBinary(os, maticeT[rowT][colT]);
    }
    os << endl;
  }
  os << ".e" << endl;

  os.close();
}

/** Jedinec::CreateMatchesFile()
- vytvori vystupni soubor matches z tohoto jedince */
void Jedinec::CreateMatchesFile() {
  int colT, colC, neg;
  char output_matches[50];
  ofstream os;

  sprintf(output_matches, "%s%s.matches", output_dir, test_name);
//  strcpy(output_matches, test_name);
//  strcat(output_matches, ".matches");

  os.open(output_matches);

  for (int r = 0; r < columnsT_matched; r++) {
    colT = rank[r];
    colC = matches[colT];
    neg = (NM[colT] + 1) %2;
    os << colC << " -> " << colT << " (" << neg << ")" << endl;
  }

  os.close();
}


/** ************************************ Populace  ************************************* */

bool SortOperator(const Jedinec& a, const Jedinec& b) {
//  cout << "a: " << a.GetFitness() << "b: " <<b.GetFitness()<< endl;
  return a.GetFitness() > b.GetFitness();
}

Populace::Populace(int pop_size) {

  size = pop_size;
  generation = 0;

  avg_fitness = -1;
  avg_cm = -1;

  best_fitness = -1;
  best_columnsT_matched = -1;

  vect.resize(size);

  Jedinec *j;
  for (unsigned int i=0; i < size; i++) {
    j = new Jedinec();
     vect[i] = *j;
    delete j;
  }
}

Populace::Populace(const Populace& s):
  size(s.size), generation(s.generation), avg_fitness(s.avg_fitness), avg_cm(s.avg_cm),
  best_fitness(s.best_fitness), best_columnsT_matched(s.best_columnsT_matched),
  vect(s.vect), best_jedinec(s.best_jedinec) {}

void Populace::Evaluate(bool use_discarding) {

  for (int i = 0; i < size; i++) {
    vect[i].CountFitness(use_discarding);
  }

  Sort();

  CountProperties();
}

void Populace::Sort() {
  sort(vect.begin(), vect.end(), SortOperator);
}

void Populace::GenerateNext(double ps, int crossover_method, double pc, double pm, 
                            double pr1, double pr2, bool use_thresholding) {
  int rodic1, rodic2, i;
  Jedinec potomek;

  generation++;

//  cout << "GN0 " << endl;
  vector<Jedinec> new_vect;
  //krizeni + mutace
  while (new_vect.size() < size) {
//    cout << "GN1: " << new_vect.size() << endl;

    rodic1 = SelectIndividual(use_thresholding);
    rodic2 = SelectIndividual(use_thresholding);
    //cout << "GN: " << i << endl;
//off << "---------------XXXXXXXXXXXXXXXXXXXXX0-------------------" << endl;
//vect[rodic1].Vypis(off);
//vect[rodic2].Vypis(off);
//    cout << "GN2: " << new_vect.size() << endl;

    if (GetRandomInterval() < pc) {
//      cout << "GN2a: " << new_vect.size() << endl;
      if (crossover_method == 1) {
        potomek = Jedinec::OnePointCrossover(vect[rodic1], vect[rodic2]);
      } else {
        potomek = Jedinec::UniformCrossover(vect[rodic1], vect[rodic2]);
      }
    } else {
//      cout << "GN2b: " << new_vect.size() << endl;
      potomek = vect[rodic1];
    }

//    cout << "GN3: " << new_vect.size() << endl;
    potomek.MatchesMutation(pm);
    potomek.RankInsertMutation(pr1);
    potomek.RankMoveMutation(pr2);

//    cout << "GN4: " << new_vect.size() << endl;
    new_vect.push_back(potomek);
//potomek1.Vypis(off);
//potomek2.Vypis(off);
//off << "---------------XXXXXXXXXXXXXXXXXXXXX0-------------------" << endl;

//    cout << "GN4: " << new_vect.size() << endl;
  }

//  cout << "GN5: " << new_vect.size() << endl;
  vect = new_vect;
//  cout << "GN6: " << new_vect.size() << endl;

}

void Populace::CountProperties() {
  double suma_f, suma_cm, f;
  int colT;
  suma_f = 0;
  suma_cm = 0;
  best_fitness = 0;
  best_columnsT_matched = 0;

  genes_amount.resize(sirkaT);
  for (colT=0; colT < sirkaT; colT++) genes_amount[colT] = 0;

  for (int i=0; i<size; i++) {
    vect[i].AdjustStatistics();

    suma_cm += vect[i].GetColumnsTMatched();

    f = vect[i].GetFitness();
    suma_f += f;
    if (f > best_fitness) best_fitness = f;
    if (vect[i].GetColumnsTMatched() > best_columnsT_matched)
      best_columnsT_matched = vect[i].GetColumnsTMatched();
    if (f > best_jedinec.GetFitness() ) {
      best_jedinec = vect[i];
      best_jedinec.Vypis(off);
      best_jedinec.CreatePLAFile();
      best_jedinec.CreateMatchesFile();
    }
    avg_fitness = suma_f / size;
    avg_cm = suma_cm / size;

    for (int j = 0; j < vect[i].GetColumnsTMatched(); j++) {
      colT = vect[i].GetRank(j);
      genes_amount[colT]++;
    }
  }
  //vypis(cout, genes_amount.begin(), genes_amount.end());
}

int Populace::SelectIndividual(bool use_thresholding) {
  int r1, r2;

  int threshold;
  int shuffle_number = sirkaT;

  r1 = rand() % size;

  if ( use_thresholding ) {
    for (int i = 0; i < shuffle_number; i++) {
      r2 = rand() % size;
      threshold = (int) ceil((double) vect[r1].GetColumnsTMatched() * vect[r2].GetColumnsTMatched() / sirkaT);
      if (threshold <= vect[r1].CommonGenes(vect[r2]) ) {
        // compete
        if ( r2 < r1 ) return r2;
        else return r1;
      }
    }
  } else {
    r2 = rand() % size;

    // compete
    if ( r2 < r1 ) return r2;
  }

  return r1;
}

void Populace::ZeroAvgValues() {
  avg_cm = 0;
  avg_fitness = 0;
}

void Populace::Vypis(ostream &os) {
  int x;
  VypisOddelovac(os);
  off << "Vypis populace " << generation << endl;

  if (size > 100) x = 100;
  else x = size;

  for (int i = 0; i<x; i++) {
    off << left << setw(7) << vect[i].GetFitness() << setw(5) << vect[i].GetColumnsTMatched();
    off << setw(5) << vect[i].GetDirectMatches() << setw(5) << vect[i].GetDirectNegMatches();
    off << setw(7) << vect[i].GetLoss();
    off << setw(2) << vect[i].GetCrossoverFlag();
    off << ">>> ";
    vect[i].VypisMatches(off);
  }

}

Jedinec Populace::Get(int i) const {
  return vect[i];
}

/** GraphLog(ostream &os)
- zaloguje hodnoty do souboru se vstupnimi hodnotami pro graf */
void Populace::GraphLog(ostream &os) {
  os << generation;
  os.put(9);
  os << best_columnsT_matched;
  os.put(9);
  os << best_fitness;
  os.put(9);
  os << avg_fitness;
  os.put(9);
  os << avg_cm << endl;
}


/** ************************************* MATICE a POLE ************************************** */

void NactiMatice(char nameC[], char nameT[]) {
  int znak;
  int pos;

  vector<vector<short int> > tmp_matrix;

  //nacetni matice C
  ifstream ifileC(nameC);
  do {
    // dalsi radek
    tmp_matrix.push_back(*new vector<short int>);
    pos=0;
    do {
      znak = ifileC.get();
      switch (znak) {
        case 48:
        case 49:
          tmp_matrix.back().push_back(znak - 48);
          pos++;
        break;
      }
    } while (ifileC && (znak!=10 || pos==0));
    if (tmp_matrix.back().size() == 0) tmp_matrix.pop_back();
  } while (ifileC);
  vyskaC = tmp_matrix.size();
  sirkaC = tmp_matrix[0].size();

  off << "vyska matice C: " << vyskaC << endl;
  off << "sirka matice C: " << sirkaC << endl;

  maticeC.CreateMatrix(vyskaC, sirkaC);
  for (int i=0; i < vyskaC; i++) {
    for (int j=0; j < sirkaC; j++) {
      maticeC[i][j] = tmp_matrix[i][j];
    }
  }

  tmp_matrix.resize(0);

    //nacetni matice T
  ifstream ifileT(nameT);
  do {
    // dalsi radek
    tmp_matrix.push_back(*new vector<short int>);
    pos=0;
    do {
      znak = ifileT.get();
      switch (znak) {
        case 48:
        case 49:
        case 50:

          tmp_matrix.back().push_back(znak - 48);
          pos++;
        break;
        case 45:
        case 88:
        case 120:
          tmp_matrix.back().push_back(2);
          pos++;
        break;
      }
    } while (ifileT && (znak!=10 || pos==0));
    if (tmp_matrix.back().size() == 0) tmp_matrix.pop_back();
  } while (ifileT);
  vyskaT = tmp_matrix.size();
  sirkaT = tmp_matrix[0].size();

  off << "vyska matice T: " << vyskaT << endl;
  off << "sirka matice T: " << sirkaT << endl;

  maticeT.CreateMatrix(vyskaT, sirkaT);
  for (int i=0; i < vyskaT; i++) {
    for (int j=0; j < sirkaT; j++) {
      maticeT[i][j] = tmp_matrix[i][j];
    }
  }

  // vytvoreni matice B
  maticeB.CreateMatrix(vyskaC, vyskaT);
  p_maticeB.CreateMatrix(vyskaC, vyskaT);
  CROW_COUNTER.CreateArray(vyskaC);
  TROW_COUNTER.CreateArray(vyskaT);

}

/** *************************************** HLAVNI PROGRAM ******************************** */
int	main(int argc, char* argv[])
{

  for (int i = 0; i < argc; i++) cout << argv[i] << " ";
  cout << endl;

  if (argc < 2) {
    cout << endl;
    cout << "Pouziti: ./bist parameter_file" << endl;
    exit(1);
  }

  srand(time(NULL));

  sprintf(test_name, "%s", argv[1]);
  //strcpy(test_name, argv[1]);
  //sprintf(output_dir, "_%s/", test_name);
  sprintf(output_dir, "");
  sprintf(output_out, "%s%s.out", output_dir, test_name);
  sprintf(output_graph, "%s%s.graph", output_dir, test_name);

//  strcpy(output_out, test_name);
//  strcat(output_out, ".out");

//  strcpy(output_graph, test_name);
//  strcat(output_graph, ".graph");

  inn.open(test_name);
  off.open(output_out);
  off_graph.open(output_graph);

  // zpracovani vstupnich parametru

  int pop_count, max_generations, pt, pop_size, max_duration, ps, crossover_method;
  bool estimate_pop_size, use_thresholding, use_discarding;
  double pc, pm, pr1, pr2;
  string tmp;
  char matrixC_name[50], matrixT_name[50];

  inn >> tmp; inn >> matrixC_name;
  inn >> tmp; inn >> matrixT_name;
  inn >> tmp; inn >> estimate_pop_size;
  inn >> tmp; inn >> max_duration;
  inn >> tmp; inn >> pop_size;
  inn >> tmp; inn >> max_generations;
  inn >> tmp; inn >> pt;
  inn >> tmp; inn >> ps;
  inn >> tmp; inn >> use_thresholding;
  inn >> tmp; inn >> crossover_method;
  inn >> tmp; inn >> pc;
  inn >> tmp; inn >> use_discarding;
  inn >> tmp; inn >> pm;
  inn >> tmp; inn >> pr1;
  inn >> tmp; inn >> pr2;
  pop_count = max_generations;

  // nacteni matice
  cout << "Nacitam matice ..." << endl;
  NactiMatice(matrixC_name, matrixT_name);
  cout << endl;

  // odhad doby vypoctu, nastaveni velikosti populace
  Jedinec *testj;
  clock_t start_ftime;
  double doba_vypoctu;
  int est_duration;

  testj = new Jedinec();
  cout << "Vypocet fitness testovaciho jedince ..." << endl;
  start_ftime = clock();
  testj->CountFitness(true);			// pro odhad pouzijeme discarding
  doba_vypoctu = (double) (clock() - start_ftime) / CLOCKS_PER_SEC;
  testj->Vypis(cout);

  cout << "Vypocet fitness trva priblizne " << doba_vypoctu << " s." << endl;

  // TODO odstranit - pouze pro testovaci ucely
  /*if (doba_vypoctu < 0.1) {
    pop_size = 1000;
  } else if (doba_vypoctu < 1) {
    pop_size = 500;
  } else if (doba_vypoctu < 6) {
    pop_size = 70;
  } else {
    pop_size = 20;
  }*/


  if (estimate_pop_size) {
    pop_size = sirkaT * (sirkaT-1) * sirkaC * (sirkaC-1) * 2  /
               testj->GetColumnsTMatched() / (testj->GetColumnsTMatched()-1);
    cout << "Odhadnuta velikost populace: " << pop_size << endl;
  } else {
    cout << "Velikost populace: " << pop_size << endl;
  }

  delete testj;

  est_duration = (int) (doba_vypoctu * pop_size * (pop_count+1));
  cout << "Odhadovana doba behu " << pop_count << " generaci: ";
  vypisCas(cout, est_duration);

  if (est_duration > max_duration * 60) {
    cout << "Pocet generaci bude upraven, aby vypocet nepresahl " << max_duration << " minut." << endl;
  }

  off << "Vstupni matice C:        " << matrixC_name << endl;
  off << "Vstupni matice T:        " << matrixT_name << endl;
  off << "Nazev testu:             " << test_name << endl;
  off << "Pocet populaci:          " << max_generations << endl;  
  off << "Velikost populace:       " << pop_size << endl;
  off << "Parametr ukonceni:       " << pt << endl;  
  off << "Parametr selekcee:       " << ps << endl;
  off << "Thresholding:            " << use_thresholding << endl;
  off << "Metoda krizeni:          " << crossover_method << endl;
  off << "Parametr krizeni:        " << pc << endl;
  off << "Discarding:              " << use_discarding << endl;
  off << "Parametr mutace matches: " << pm << endl;
  off << "Parametr mutace rank1:   " << pr1 << endl;
  off << "Parametr mutace rank2:   " << pr2 << endl;

  matches_statistics.CreateMatrix(sirkaT, sirkaC);
  matches_statistics.SetConstValue(0);

  rank_statistics.CreateMatrix(sirkaT, sirkaT);
  rank_statistics.SetConstValue(0);

//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

  Populace* act_population = NULL;
  Jedinec best;
  int pt_counter = 0; 
  double best_avg_fitness = 0;

  start_time = time(NULL);

  act_population = new Populace(pop_size);
  act_population->Evaluate(use_discarding);
  act_population->GraphLog(off_graph);
  act_population->Vypis(off);
  cout << "------------------------ " << act_population->generation << " ------------------------" << endl;

  if (max_generations > 0)
  do {

    act_population->GenerateNext(ps, crossover_method, pc, pm, pr1, pr2, use_thresholding);

    act_population->Evaluate(use_discarding);

    act_population->Get(0).Check();
    act_population->GraphLog(off_graph);
    cout << "------------------------ " << act_population->generation << " ------------------------" << endl;

    if (best.GetFitness() < act_population->best_jedinec.GetFitness()) 
      best = act_population->best_jedinec;

    if (best_avg_fitness + 0.0001 > act_population->avg_fitness) pt_counter++;
    else {
      pt_counter = 0;
      best_avg_fitness = act_population->avg_fitness;
    }
    if (pt_counter >= pt) break;
    if (act_population->generation >= max_generations) break;
    if ((int) best.GetFitness() == sirkaT + 1) break;
    if (time(NULL) - start_time >= max_duration * 60) break;

    if (act_population->generation % 5 == 1) act_population->Vypis(off);

  } while (1 == 1);

  act_population->Vypis(off);

  end_time = time(NULL);
  cout << "Vysledny cas: ";
  vypisCas(cout, end_time - start_time);
  cout << endl;

  best.Check();
  best.Vypis(cout);

  ////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////

  // zalogujeme statistiky do detailniho vystupu
  VypisOddelovac(off);
  VypisStatistiku(off);
  VypisOddelovac(off);
  off << "Vysledny cas: ";
  vypisCas(off, end_time - start_time);
  off << endl;
  VypisOddelovac(off);
  off << "Nejlepsi nalezeny jedinec: columns_matched = " << best.GetColumnsTMatched() << endl;
  best.Vypis(off);
  VypisOddelovac(off);
// zalogujeme vysledky do souhurneho vystupu
/*  off_all.open("test_output", ios::app);
  off_all << left << setw(25) << matrixC_name << setw(25) << matrixT_name;
  off_all << left << setw(25) << test_name;
  off_all << right << setw(6) << pop_size << setw(6) << act_population->generation;
  off_all << setw(5) << ps;
  off_all << setw(5) << use_thresholding;
  off_all << setw(5) << crossover_method;
  off_all << setw(5) << pc;
  off_all << setw(5) << use_discarding;
  off_all << setw(5) << pm;
  off_all << setw(5) << pr1;
  off_all << setw(5) << pr2;
  off_all << setw(8) << best.GetFitness();
  off_all << setw(5) << best.GetColumnsTMatched();
  off_all << setw(5) << best.GetDirectMatches();
  off_all << setw(5) << best.GetDirectNegMatches();
  off_all << setw(10) << end_time - start_time << "   " << test_name << endl;*/

  best.CreatePLAFile();
  best.CreateMatchesFile();

  return 0;

}
