#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "windows.h"
#include "psapi.h"

/**
 * @brief The MainWindow constructor constructs class and set up basics of GUI
 */
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->filePath->setEnabled(false);

    ui->KnnOrKvalueEditLine->hide();
    ui->KnnOrKvalue->hide();
    ui->numberOfTrees->hide();
    ui->numberOfTreesEditLine->hide();

    ui->NumOfAtributes->hide();
    ui->NumOfInstances->hide();
    ui->atributes->hide();
    ui->instances->hide();

    ui->UseTrainingSet->setChecked(true);
    ui->PercentageSplitEditLine->setEnabled(false);
    ui->CrossValidationEditLine->setEnabled(false);

    ui->output->setReadOnly(true);
    ui->saveResults->setEnabled(false);
    ui->run->setEnabled(false);

    // changing random funciton
      srand(1); // one means random changes only if the new dataset is loaded
}

/**
 * @brief The MainWindow destructor destructs GUI and clears the Data
 */
MainWindow::~MainWindow()
{
    delete ui;
    Data.clear();
}

/**
 * @brief The fillVector function fills vector from csv file
 * @param fileName - name of file
 */
void MainWindow::fillVector(std::string fileName)
{
        std::string line, field;
        std::vector<std::string> row;
        std::ifstream file;
        QMessageBox msgBox;

        file.open(fileName.c_str());

        if (file.is_open()) {
            while ( std::getline(file, line) )  // get next line in file
            {
                    std::istringstream strm(line);

                    while ( std::getline(strm, field, ',') )  // break line into comma delimitted fields
                    {
                    row.push_back(field);  // add each field to the 1D array
                    }
                    Data.push_back(row);  // add the 1D array to the 2D array         
                    row.clear();
                    row.shrink_to_fit();
            }
         } else {
            msgBox.setText("Nelze číst soubor");
            msgBox.setIcon(QMessageBox::Critical);
            msgBox.exec();
        }

      file.close();

      std::random_shuffle(Data.begin()+1, Data.end()); //shuffles vector for better validation

      Data = handleMissingValues(Data); // replacing missing values for data
}

/**
 * @brief The countInstances function counting number of instances in data
 * @return integer - number of instances
 */
int MainWindow::numberOfInstances()
{
    return Data.size()-1;
}

/**
 * @brief The countAtributes function counting number of atributes in data
 * @return integer - number of atributes
 */
int MainWindow::numberOfAtributes()
{
    return Data[0].size();
}

/**
 * @brief The sumColumn function sums given numeric column
 * @param Data - all data
 * @param column - index of column to sum
 * @return double - sum of numeric column
 */
double MainWindow::sumColumn(std::vector < std::vector<std::string> > Data, int column) {

     std::string strData;
     double pom, pomData;

        for (size_t j = 1; j < Data.size(); ++j) {
            strData = Data[j][column]; // converting string to double
            pomData = atof(strData.c_str());
            pom = pom + pomData;
        }

        return pom;
}

/**
 * @brief The handleMissingValues function replacing missing values in data vector
 * @param Data - all data to repclace
 * @return vector - data without missing values
 */
std::vector < std::vector<std::string> > MainWindow::handleMissingValues(std::vector < std::vector<std::string> > Data) {

    std::vector <std::string> column;
    std::string maxS, str;
    double pom, fill;

    for (size_t i = 0; i < Data[0].size(); ++i) {
        str = Data[1][i];
        std::vector<char> writable(str.begin(), str.end()); // converting string to vector of chars

        if (isdigit(writable[0])){ // if first char is digit
                pom = sumColumn(Data, i);

                 for (size_t j = 1; j < Data.size(); ++j) {
                        if (Data[j][i] == "?") {
                            fill = pom / numberOfInstances();
                            std::ostringstream s;
                            s << fill;
                            std::string strn(s.str()); // converting double to string
                            s.flush();
                            std::replace(Data[j].begin(), Data[j].end(), Data[j][i], strn); // replacing missing values with mean
                        }
                 }
            } else {
       for (size_t j = 1; j < Data.size(); ++j) {
            if (Data[j][i] == "?") {
               for (size_t k = 0; k < Data.size(); ++k) {
                        column.push_back(Data[k][i]);
               }

               maxS = returnOneClassFromMany(column);
               for (size_t k = 1; k < Data.size(); ++k) {
                        std::replace(Data[k].begin(), Data[k].end(), Data[k][i], maxS); // replacing missing values with instance with maximum count
               }

               column.clear();
            }

        }
      }
    }
    return Data;
}

/**
 * @brief The discretize function replacing numeric values with nominal
 * @param Data - all data to repclace
 * @param numberOfClasses - number of classes for dividing
 * @return vector - data without numeric atributes
 */
std::vector < std::vector<std::string> > MainWindow::discretize(std::vector < std::vector<std::string> > Data, int numberOfClasses) {

    std::vector <std::string> columnStr;
    std::vector <double> column, pomVec, pomColumn;
    std::string str, strData;
    double min, max, pom, firstClass, difference, sumClass;

    for (size_t i = 0; i < Data[0].size(); ++i) {
        str = Data[1][i];
        std::vector<char> writable(str.begin(), str.end()); // converting string to vector of chars

        if (isdigit(writable[0])){ // if first char is digit
                 for (size_t j = 1; j < Data.size(); ++j) {
                        strData = Data[j][i];
                        pom = atof(strData.c_str()); // converting string to double

                        column.push_back(pom);
                 }

                 std::sort(column.begin(), column.end()); // sorting column

                 min = countMin(column);
                 max = countMax(column);

                 firstClass = (min * (numberOfClasses - 1) + max)/numberOfClasses; // counting first class to divide instances

                 difference = firstClass - min; // counting difference to divide other classes

                 pomVec.push_back(firstClass);
                 sumClass = firstClass;

                 for (int l = 0; l < numberOfClasses-1; ++l) {
                     sumClass = sumClass + difference; // making all other classes
                     pomVec.push_back(sumClass);
                 }

                 // pushing classes to column
                 for (size_t k = 0; k < pomVec.size(); ++k) {
                   while (column[0] <= pomVec[k] && column[0] != column.back()) {
                        pomColumn.push_back(pomVec[k]);
                        column.erase(std::find(column.begin(), column.end(), column[0]));
                   }
                 }

                 pomColumn.push_back(pomVec.back()); //input last member at the end of vector

                 for (size_t l = 0; l < pomColumn.size(); ++l) {
                         std::ostringstream s;
                         s << pomColumn[l];
                         std::string strn(s.str()); // converting double back to string
                         columnStr.push_back(strn);
                         s.flush();
                 }

                 for (size_t k = 0; k < columnStr.size(); ++k) {
                         std::replace(Data[k+1].begin(), Data[k+1].end(), Data[k+1][i], columnStr[k]); //replacing column with classes
                 }

                 pomColumn.clear();
                 pomVec.clear();
                 column.clear();
                 columnStr.clear();

            }
    }
    return Data;
}

/**
 * @brief The countClassesPercantage function counts probabilities of classes
 * @param Data - all data to count
 * @return map - name of class with ist probability
 */
std::map <std::string, double> MainWindow::countClassesPercantage(std::vector < std::vector<std::string> > Data) {

    std::map <std::string, double> cls;

    for (size_t i = 1; i < Data.size(); i++) {
         cls.insert(std::pair<std::string, double>(Data[i][numberOfAtributes()-1], cls[Data[i][numberOfAtributes()-1]]++)); // counting counts of instances
    }

   for(std::map<std::string, double>::iterator it = cls.begin(); it != cls.end(); ++it) {
       cls.insert(std::pair<std::string, double>(it->first, it->second = it->second/numberOfInstances())); // counting probabilities of instances
    }
    return cls;
}

/**
 * @brief The on_browse_clicked procedure handling clicking on browse button
 */
void MainWindow::on_browse_clicked()
{
    //opens dialog for choosing file
    QString fileName =
            QFileDialog::getOpenFileName(this, tr("Open File"),
                                         QDir::homePath(),
                                         tr("Text files (*.txt *.csv)"));
    if (!fileName.isEmpty()) {
    ui->filePath->setText(fileName);

    Data.clear();
    Data.shrink_to_fit();

    fillVector(fileName.toStdString());

    }

    // showing number of atributes
    QString atributes = QString::number(numberOfAtributes());
    ui->NumOfAtributes->setText(atributes);
    ui->NumOfAtributes->show();
    ui->atributes->show();

    // showing number of instances
    QString instances = QString::number(numberOfInstances());
    ui->NumOfInstances->setText(instances);
    ui->NumOfInstances->show();
    ui->instances->show();

    ui->run->setEnabled(true);
}

/**
 * @brief The on_Algoritms_activated procedure handling choosing algoritm
 * @param &arg1 - name of algoritm
 */
void MainWindow::on_Algoritms_activated(const QString &arg1)
{

    if (arg1 == "IBk") {
       ui->KnnOrKvalue->show();
       ui->KnnOrKvalueEditLine->show();
       ui->numberOfTrees->hide();
       ui->numberOfTreesEditLine->hide();

       ui->KnnOrKvalue->setText("Pocet sousedů: ");
       ui->KnnOrKvalueEditLine->setText("3");
       ui->KnnOrKvalueEditLine->setValidator( new QIntValidator(1, 100, this) ); // determining min and max value to be written in line
       ui->KnnOrKvalueEditLine->setToolTip("Vyberte počet nejbližších sousedů.");

    } else
    if (arg1 == "RandomForest") {
        ui->KnnOrKvalue->show();
        ui->KnnOrKvalueEditLine->show();

        ui->KnnOrKvalue->setText("k-Hodnota: ");
        ui->KnnOrKvalueEditLine->setText("2");
        ui->KnnOrKvalueEditLine->setValidator( new QIntValidator(1, 10000, this) ); // determining min and max value to be written in line
        ui->KnnOrKvalueEditLine->setToolTip("kHodnota - kolik atributů bude alogritmus brát v potaz při počítání entropie.");

        ui->numberOfTrees->show();
        ui->numberOfTreesEditLine->show();

        ui->numberOfTrees->setText("Počet stromů: ");
        ui->numberOfTreesEditLine->setText("10");
        ui->numberOfTreesEditLine->setToolTip("Vyberte počet stromů."); // determining min and max value to be written in line
        ui->numberOfTreesEditLine->setValidator( new QIntValidator(1, 10000, this) );

    } else {
        ui->KnnOrKvalueEditLine->clear();
        ui->numberOfTreesEditLine->clear();
        ui->numberOfTrees->clear();
        ui->KnnOrKvalue->clear();

        ui->KnnOrKvalueEditLine->hide();
        ui->KnnOrKvalue->hide();
        ui->numberOfTrees->hide();
        ui->numberOfTreesEditLine->hide();
    }
}

/**
 * @brief The on_PercentageSplit_clicked procedure handling choosing percentage split radion button
 */
void MainWindow::on_PercentageSplit_clicked()
{
    ui->CrossValidation->setChecked(false);
    ui->CrossValidationEditLine->setEnabled(false);
    ui->PercentageSplitEditLine->setEnabled(true);
    ui->PercentageSplitEditLine->setText("66");
    ui->PercentageSplitEditLine->setValidator( new QIntValidator(1, 99, this) ); // determining min and max value to be written in line
    ui->PercentageSplitEditLine->setToolTip("Procentuální rozdělení musí být větší než 0 a menší než 100.");
}

/**
 * @brief The on_CrossValidation_clicked procedure handling choosing cross validation radion button
 */
void MainWindow::on_CrossValidation_clicked()
{
    ui->PercentageSplitEditLine->setEnabled(false);
    ui->CrossValidationEditLine->setEnabled(true);
    ui->CrossValidationEditLine->setText("10");
    ui->CrossValidationEditLine->setValidator( new QIntValidator(1, numberOfInstances()-1, this) ); // determining min and max value to be written in line
    ui->CrossValidationEditLine->setToolTip("Počet foldů musí být menší než počet atributů a větší jak nula.");
}

/**
 * @brief The on_UseTrainingSet_clicked procedure handling choosing training set radion button
 */
void MainWindow::on_UseTrainingSet_clicked()
{
    ui->CrossValidation->setChecked(false);
    ui->PercentageSplit->setChecked(false);
    ui->PercentageSplitEditLine->setEnabled(false);
    ui->CrossValidationEditLine->setEnabled(false);
}

/**
 * @brief The on_run_clicked procedure handling clicking on run button
 */
void MainWindow::on_run_clicked()
{
    const int numDiscr = 10;

    trainTestPair BigData;
    SplittingMethod split(numberOfInstances());
    std::map <std::string, double> cls = countClassesPercantage(Data);

    std::vector < std::vector<std::string> > trainData;
    std::vector < std::vector<std::string> > testData;

    std::vector <std::string> testToCompare;
    std::vector <std::string> pomTest;

    QTime myTime;

   // myTime.start(); // starting timer

    ui->saveResults->setEnabled(true);

   if (ui->Algoritms->currentText() == "NaiveBayes") {

       Data = discretize(Data, numDiscr); // discreditize numeric atributes

       NaiveBayes bayes(cls);
       std::vector <probabilityPair> DataProbabilities;

       if (ui->UseTrainingSet->isChecked()) {

         BigData = split.useTrainingSet(Data); //spliting data to test and training sets

         trainData = BigData.first;
       //  testData = BigData.second;

         for (size_t i = 0; i < trainData.size(); i++) {
             testToCompare.push_back(trainData[i][numberOfAtributes()-1]); // adding actual test classes to vector
         }

         myTime.start(); // starting timer

         DataProbabilities = bayes.Train(trainData);

         pomTest = bayes.Classify(trainData, DataProbabilities);

         int nTime = myTime.elapsed(); // counting elapsed time

         Results(testToCompare, pomTest, nTime);
         testToCompare.clear();

       } else
           if (ui->PercentageSplit->isChecked()) {

               QString percentageSplitStr = ui->PercentageSplitEditLine->text();

               if (!percentageSplitStr.isEmpty()) {

                BigData = split.percentageSplit(Data, percentageSplitStr.toInt()); //spliting data to test and training sets

                trainData = BigData.first;
                testData = BigData.second;

                for (size_t i = 0; i < testData.size(); i++) {
                   testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
                }

                myTime.start(); // starting timer

                DataProbabilities = bayes.Train(trainData);

                pomTest = bayes.Classify(testData, DataProbabilities);

                int nTime = myTime.elapsed(); // counting elapsed time

                Results(testToCompare, pomTest, nTime);
                testToCompare.clear();

               }else {
                   QMessageBox msgBox;
                   msgBox.setText("Procentuální rozdělení musí být větší než 0 a menší než 100.");
                   msgBox.setIcon(QMessageBox::Warning);
                   msgBox.exec();
               }

           } else
               if (ui->CrossValidation->isChecked()) {

                   std::vector < std::vector<std::string> > foldsTest;

                   QString crossValidationStr = ui->CrossValidationEditLine->text();

                   int numberOfSets = numberOfInstances() / crossValidationStr.toInt(); // counting number of sets

                   int rest = numberOfInstances() - numberOfSets*crossValidationStr.toInt(); // counting rest of data

                    if (!ui->CrossValidationEditLine->text().isEmpty()) {

                        myTime.start(); // starting timer

                      for (int i = 0; i < crossValidationStr.toInt(); i++) {

                          // spliting to test and train set
                          if (i == (crossValidationStr.toInt()-rest)) {
                            numberOfSets = numberOfSets + 1;
                            BigData = split.crossValidation(Data, numberOfSets);
                          } else
                              BigData = split.crossValidation(Data, numberOfSets);

                          trainData = BigData.first;
                          testData = BigData.second;

                          for (size_t j = 0; j < testData.size(); j++) {
                             testToCompare.push_back(testData[j][numberOfAtributes()-1]); // adding actual test classes to vector
                          }

                          DataProbabilities = bayes.Train(trainData);

                          pomTest = bayes.Classify(testData, DataProbabilities);

                          foldsTest.push_back(pomTest); // adding predicted classes to help vector

                          pomTest.clear();

              }

                      for (size_t i = 0; i < foldsTest.size(); i++) {
                          for (size_t j = 0; j < foldsTest[i].size(); j++) {
                              pomTest.push_back(foldsTest[i][j]); // adding predicted classes
                          }
                      }

                      int nTime = myTime.elapsed(); // counting elapsed time

                      Results(testToCompare, pomTest, nTime);
                      testToCompare.clear();

          } else {
                        QMessageBox msgBox;
                        msgBox.setText("Počet foldů musí být menší než počet atributů a větší jak nula.");
                        msgBox.setIcon(QMessageBox::Warning);
                        msgBox.exec();
                    }
      }
   } else if (ui->Algoritms->currentText() == "ID3") {

       Data = discretize(Data, numDiscr); // discreditize numeric atributes

       std::vector <std::string> header;
       header = split.SaveHeader(Data);

       ID3 id3(cls, header);

       std::vector <std::string> Tree;

       if (ui->UseTrainingSet->isChecked()) {

           BigData = split.useTrainingSet(Data); //spliting data to test and training sets

           trainData = BigData.first;
           testData = BigData.second;

           for (size_t i = 0; i < testData.size(); i++) {
               testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
           }

           trainData.erase(trainData.begin());

           Tree = id3.Train(trainData);

           pomTest = id3.Classify(testData, Tree, testToCompare);

           int nTime = myTime.elapsed(); // counting elapsed time

           Results(testToCompare, pomTest, nTime);
           testToCompare.clear();

       } else
           if (ui->PercentageSplit->isChecked()) {

               QString percentageSplitStr = ui->PercentageSplitEditLine->text();

               if (!percentageSplitStr.isEmpty()) {

                BigData = split.percentageSplit(Data, percentageSplitStr.toInt()); //spliting data to test and training sets

                trainData = BigData.first;
                testData = BigData.second;

                for (size_t i = 0; i < testData.size(); i++) {
                   testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
                }

                trainData.erase(trainData.begin());

                Tree = id3.Train(trainData);

                pomTest = id3.Classify(testData, Tree, testToCompare);

                int nTime = myTime.elapsed(); // counting elapsed time

                Results(testToCompare, pomTest, nTime);
                testToCompare.clear();

               }else {
                   QMessageBox msgBox;
                   msgBox.setText("Procentuální rozdělení musí být větší než 0 a menší než 100.");
                   msgBox.setIcon(QMessageBox::Warning);
                   msgBox.exec();
               }

           } else
                if (ui->CrossValidation->isChecked()) {
                          std::vector < std::vector<std::string> > foldsTest;

                          QString crossValidationStr = ui->CrossValidationEditLine->text();

                          int numberOfSets = numberOfInstances() / crossValidationStr.toInt(); // counting number of sets

                          int rest = numberOfInstances() - numberOfSets*crossValidationStr.toInt();  // counting rest of data

                           if (!ui->CrossValidationEditLine->text().isEmpty()) {

                             for (int i = 0; i < crossValidationStr.toInt(); i++) {

                                 // spliting to test and train set
                                 if (i == (crossValidationStr.toInt()-rest)) {
                                   numberOfSets = numberOfSets + 1;
                                   BigData = split.crossValidation(Data, numberOfSets);
                                 } else
                                     BigData = split.crossValidation(Data, numberOfSets);

                                 trainData = BigData.first;
                                 testData = BigData.second;

                                 for (size_t j = 0; j < testData.size(); j++) {
                                    testToCompare.push_back(testData[j][numberOfAtributes()-1]); // adding actual test classes to vector
                                 }

                                 if (trainData[0][0] == header[0]) {
                                     trainData.erase(trainData.begin());
                                 }

                                 Tree = id3.Train(trainData);

                                 pomTest = id3.Classify(testData, Tree, testToCompare);

                                 foldsTest.push_back(pomTest); // adding predicted classes to help vector

                                 pomTest.clear();

                            }

                             for (size_t i = 0; i < foldsTest.size(); i++) {
                                 for (size_t j = 0; j < foldsTest[i].size(); j++) {
                                     pomTest.push_back(foldsTest[i][j]); // adding predicted classes
                                 }
                             }

                             int nTime = myTime.elapsed(); // counting elapsed time

                             Results(testToCompare, pomTest, nTime);
                             testToCompare.clear();

                        } else {
                               QMessageBox msgBox;
                               msgBox.setText("Počet foldů musí být menší než počet atributů a větší jak nula.");
                               msgBox.setIcon(QMessageBox::Warning);
                               msgBox.exec();
                           }
                      }

   } else if (ui->Algoritms->currentText() == "RandomForest") {

       Data = discretize(Data, numDiscr); // discreditize numeric atributes

       std::vector <std::string> Tree;
       std::vector <std::string> header;

       header = split.SaveHeader(Data);

       RandomForest randomForest(cls, header);

       QString numberOfTrees = ui->numberOfTreesEditLine->text();
       QString kVal = ui->KnnOrKvalueEditLine->text();
       unsigned int kValue = kVal.toInt();

       if (ui->UseTrainingSet->isChecked()) {

           BigData = split.useTrainingSet(Data); //spliting data to test and training sets

           trainData = BigData.first;
           testData = BigData.second;

           for (size_t i = 0; i < testData.size(); i++) {
               testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
           }

           trainData.erase(trainData.begin());

           myTime.start(); // starting timer

           Tree = randomForest.Train(trainData, numberOfTrees.toInt(), kValue);

           pomTest = randomForest.Classify(testData, Tree, numberOfTrees.toInt());

           int nTime = myTime.elapsed(); // counting elapsed time

           Results(testToCompare, pomTest, nTime);
           testToCompare.clear();

       }else
           if (ui->PercentageSplit->isChecked()) {

               QString percentageSplitStr = ui->PercentageSplitEditLine->text();

               if (!percentageSplitStr.isEmpty()) {

                BigData = split.percentageSplit(Data, percentageSplitStr.toInt()); //spliting data to test and training sets

                trainData = BigData.first;
                testData = BigData.second;

                for (size_t i = 0; i < testData.size(); i++) {
                   testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
                }

                trainData.erase(trainData.begin());

                myTime.start(); // starting timer

                Tree = randomForest.Train(trainData, numberOfTrees.toInt(), kValue);

                pomTest = randomForest.Classify(testData, Tree, numberOfTrees.toInt());

                int nTime = myTime.elapsed(); // counting elapsed time

                Results(testToCompare, pomTest, nTime);
                testToCompare.clear();

               }else {
                   QMessageBox msgBox;
                   msgBox.setText("Procentuální rozdělení musí být větší než 0 a menší než 100.");
                   msgBox.setIcon(QMessageBox::Warning);
                   msgBox.exec();
               }

           } else
               if (ui->CrossValidation->isChecked()) {
                         std::vector < std::vector<std::string> > foldsTest;

                         QString crossValidationStr = ui->CrossValidationEditLine->text();

                         int numberOfSets = numberOfInstances() / crossValidationStr.toInt(); // counting number of sets

                         int rest = numberOfInstances() - numberOfSets*crossValidationStr.toInt();  // counting rest of data

                         myTime.start(); // starting timer

                          if (!ui->CrossValidationEditLine->text().isEmpty()) {

                            for (int i = 0; i < crossValidationStr.toInt(); i++) {

                                // spliting to test and train set
                                if (i == (crossValidationStr.toInt()-rest)) {
                                  numberOfSets = numberOfSets + 1;
                                  BigData = split.crossValidation(Data, numberOfSets);
                                } else
                                    BigData = split.crossValidation(Data, numberOfSets);

                                trainData = BigData.first;
                                testData = BigData.second;

                                for (size_t j = 0; j < testData.size(); j++) {
                                   testToCompare.push_back(testData[j][numberOfAtributes()-1]); // adding actual test classes to vector
                                }

                                if (trainData[0][0] == header[0]) {
                                    trainData.erase(trainData.begin());
                                }

                                Tree = randomForest.Train(trainData, numberOfTrees.toInt(), kValue);

                                pomTest = randomForest.Classify(testData, Tree, numberOfTrees.toInt());

                                foldsTest.push_back(pomTest); // adding predicted classes to help vector

                                pomTest.clear();

                           }

                            for (size_t i = 0; i < foldsTest.size(); i++) {
                                for (size_t j = 0; j < foldsTest[i].size(); j++) {
                                    pomTest.push_back(foldsTest[i][j]); // adding predicted classes
                                }
                            }

                            int nTime = myTime.elapsed(); // counting elapsed time

                            Results(testToCompare, pomTest, nTime);
                            testToCompare.clear();

                       } else {
                              QMessageBox msgBox;
                              msgBox.setText("Počet foldů musí být menší než počet atributů a větší jak nula.");
                              msgBox.setIcon(QMessageBox::Warning);
                              msgBox.exec();
                          }
                     }
   } else if (ui->Algoritms->currentText() == "IB1" || ui->Algoritms->currentText() == "IBk") {

       IBk ibk;

       std::vector <std::string> header;

       header = split.SaveHeader(Data);

       QString numberOfNeighbours = ui->KnnOrKvalueEditLine->text();
       unsigned int k = numberOfNeighbours.toInt();

       if (ui->UseTrainingSet->isChecked()) {

           BigData = split.useTrainingSet(Data); //spliting data to test and training sets

           trainData = BigData.first;
           testData = BigData.second;

           for (size_t i = 0; i < testData.size(); i++) {
               testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
           }

           trainData.erase(trainData.begin());

           myTime.start(); // starting timer

           trainData = ibk.Train(trainData);

           // determining nereast neighbours
           if (ui->Algoritms->currentText() == "IB1") {
               k = 1;
               pomTest = ibk.Classify(trainData, k);
           } else {
               k = numberOfNeighbours.toInt();
               pomTest = ibk.Classify(trainData, k);
           }

           int nTime = myTime.elapsed(); // counting elapsed time

           Results(testToCompare, pomTest, nTime);
           testToCompare.clear();

       } else
          if (ui->PercentageSplit->isChecked()) {

              QString percentageSplitStr = ui->PercentageSplitEditLine->text();

              if (!percentageSplitStr.isEmpty()) {

               BigData = split.percentageSplit(Data, percentageSplitStr.toInt()); //spliting data to test and training sets

               trainData = BigData.first;
               testData = BigData.second;

               for (size_t i = 0; i < testData.size(); i++) {
                  testToCompare.push_back(testData[i][numberOfAtributes()-1]); // adding actual test classes to vector
               }

               trainData.erase(trainData.begin());

               myTime.start(); // starting timer

               trainData = ibk.Train(trainData);
               testData = ibk.Train(testData);

               // determining nereast neighbours
               if (ui->Algoritms->currentText() == "IB1") {
                   k = 1;
                   pomTest = ibk.Classify(testData, k);
               } else {
                   k = numberOfNeighbours.toInt();
                   pomTest = ibk.Classify(testData, k);
               }

               int nTime = myTime.elapsed(); // counting elapsed time

               Results(testToCompare, pomTest, nTime);
               testToCompare.clear();

              }else {
                  QMessageBox msgBox;
                  msgBox.setText("Procentuální rozdělení musí být větší než 0 a menší než 100.");
                  msgBox.setIcon(QMessageBox::Warning);
                  msgBox.exec();
              }
          } else
              if (ui->CrossValidation->isChecked()) {

                        std::vector < std::vector<std::string> > foldsTest;

                        QString crossValidationStr = ui->CrossValidationEditLine->text();

                        int numberOfSets = numberOfInstances() / crossValidationStr.toInt(); // counting number of sets

                        int rest = numberOfInstances() - numberOfSets*crossValidationStr.toInt();  // counting rest of data

                         if (!ui->CrossValidationEditLine->text().isEmpty()) {

                           myTime.start(); // starting timer

                           for (int i = 0; i < crossValidationStr.toInt(); i++) {

                               // spliting to test and train set
                               if (i == (crossValidationStr.toInt()-rest)) {
                                 numberOfSets = numberOfSets + 1;
                                 BigData = split.crossValidation(Data, numberOfSets);
                               } else
                                   BigData = split.crossValidation(Data, numberOfSets);

                               trainData = BigData.first;
                               testData = BigData.second;

                               for (size_t j = 0; j < testData.size(); j++) {
                                  testToCompare.push_back(testData[j][numberOfAtributes()-1]); // adding actual test classes to vector
                               }

                               trainData.erase(trainData.begin());

                               trainData = ibk.Train(trainData);
                               testData = ibk.Train(testData);

                               // determining nereast neighbours
                               if (ui->Algoritms->currentText() == "IB1") {
                                   k = 1;
                                   pomTest = ibk.Classify(testData, k);
                               } else {
                                   unsigned int num = numberOfNeighbours.toInt();
                                   if (num >= testData.size()) {
                                      k = testData.size();
                                   } else k = numberOfNeighbours.toInt();

                                   pomTest = ibk.Classify(testData, k);
                               }

                               foldsTest.push_back(pomTest); // adding predicted classes to help vector

                               pomTest.clear();

                          }

                           for (size_t i = 0; i < foldsTest.size(); i++) {
                               for (size_t j = 0; j < foldsTest[i].size(); j++) {
                                   pomTest.push_back(foldsTest[i][j]); // adding predicted classes
                               }
                           }

                           int nTime = myTime.elapsed(); // counting elapsed time

                           Results(testToCompare, pomTest, nTime);
                           testToCompare.clear();

                      } else {
                             QMessageBox msgBox;
                             msgBox.setText("Počet foldů musí být menší než počet atributů a větší jak nula.");
                             msgBox.setIcon(QMessageBox::Warning);
                             msgBox.exec();
                         }
                    }
   }
}

/**
 * @brief The Results procedure printing out resutls and save them to file
 * @param testToCompare - actual test classes
 * @param pomTest - predicted test classes
 * @param nTime - how long prediction last
 */
void MainWindow::Results(std::vector <std::string> testToCompare, std::vector <std::string> pomTest, int nTime) {

    std::vector <int> conf;
    std::vector < std::vector <int> > confMatrix;
    std::vector <std::string> pomClass;

    std::map <std::string, double> cls = countClassesPercantage(Data);

    std::vector <double> TPrate, FPrate, Precision, Recall, Fmeasure;

    int countRight = 0;
    int countWrong = 0;
    int countWhole = 0;
    double percentageRight = 0;
    double percentageWrong = 0;
    int pom = 0;

    for (size_t i = 0; i < pomTest.size(); ++i) {
        if (pomTest[i] == testToCompare[i]) {
            countRight++; // correct classification

        } else {
            countWrong++; // incorrect classification
        }
    }

    countWhole = countRight + countWrong;
    percentageRight = roundf((double) countRight / (double) countWhole * 100);
    percentageWrong = roundf((double) countWrong / (double) countWhole * 100);

    for(std::map<std::string, double>::iterator it = cls.begin(); it != cls.end(); ++it) {
        pomClass.push_back(it->first);
    }

    // computing confusion matrix
    for (size_t j = 0; j < pomClass.size(); j++) {
        for(std::map<std::string, double>::iterator it = cls.begin(); it != cls.end(); ++it) {
        for (size_t i = 0; i < pomTest.size(); i++) {
             if (testToCompare[i] == pomClass[j]) {
                  if (pomTest[i] == it->first) {
                   pom++;
              }
         }
        }
     conf.push_back(pom);
     pom = 0;
     }
     confMatrix.push_back(conf);
     conf.clear();
     }

    double row, column, totalCount;
    int rowCount;

     //Counting TP rate
    for (size_t i = 0; i < confMatrix.size(); ++i) {
        for (size_t j = 0; j < confMatrix[i].size(); ++j) {
            row = row + confMatrix[i][j];
     }
        if (confMatrix[i][i] != 0) {
            TPrate.push_back(roundf(confMatrix[i][i]/row * 100) / 100);
            row = 0;
        } else TPrate.push_back(0);
    }

    column = 0;
    rowCount = 0;

    //Counting FP rate
    for (size_t i = 0; i < confMatrix.size(); ++i) {
        for (size_t j = 0; j < confMatrix[i].size(); ++j) {
            rowCount = rowCount + confMatrix[j][i];
     }
    }

     for (size_t i = 0; i < confMatrix.size(); ++i) {
      for (size_t j = 0; j < confMatrix[i].size(); ++j) {
            row = row + confMatrix[i][j];
            column = column + confMatrix[j][i];
      }

        column = column - confMatrix[i][i] ;
        row = rowCount - row;

        FPrate.push_back(roundf(column/row * 100) / 100);
        column = 0;
        row = 0;
    }

    rowCount = 0;

    //Counting precision
    for (size_t i = 0; i < confMatrix.size(); ++i) {
        for (size_t j = 0; j < confMatrix[i].size(); ++j) {
            column = column + confMatrix[j][i];
     }
        if (column != 0) {
            Precision.push_back(roundf(confMatrix[i][i]/column * 100) / 100);
            column = 0;
        } else Precision.push_back(0);

    }

    //Counting Recall
     Recall = TPrate;

     totalCount = 0;

    //Counting F-Measure
    for (size_t i = 0; i < Recall.size(); ++i) {
         if ((Recall[i]+Precision[i]) != 0) {
          totalCount = roundf(2*((Recall[i]*Precision[i])/(Recall[i]+Precision[i])) * 100) / 100;
          Fmeasure.push_back(totalCount);
         } else Fmeasure.push_back(0);
    }

     int diagonal = 0;
     double PrA = 0;
     double PrE = 0;
     double Kappa = 0;

    //counting kappa-statistics
     for (size_t i = 0; i < confMatrix.size(); ++i) {
         diagonal = diagonal + confMatrix[i][i];
       }

     PrA = (float) diagonal/ (float) countWhole;

    for (size_t i = 0; i < confMatrix.size(); ++i) {
     for (size_t j = 0; j < confMatrix[i].size(); ++j) {
         row = row + confMatrix[i][j];
         column = column + confMatrix[j][i];
     }

     PrE = PrE + ((row/countWhole)*(column/countWhole));
     column = 0;
     row = 0;
    }

     Kappa = roundf((PrA - PrE)/(1-PrE) * 100) / 100;

     ui->output->clear();
     QString filename = "tmp.txt";
     // printing resutls to temp file
     QFile fileTo(filename);
     if (fileTo.open(QIODevice::WriteOnly | QIODevice::Text)){
      QTextStream out(&fileTo);
        out << "Results:" << endl;
        out << endl;
        out << "Correctly classified instances: " << countRight << "   Percentage: " << percentageRight << "%" << endl;
        out << "Incorrectly classified instances: " << countWrong << "   Percentage: " << percentageWrong << "%" << endl;

        out << endl;
        out << "===Confusion Matrix===" << endl;
        out << endl;

        for (size_t i = 0; i < confMatrix.size(); ++i) {
              out << QString::fromStdString(pomClass[i]) << ": ";
              for (size_t j = 0; j < confMatrix[i].size(); ++j) {
                        out << confMatrix[i][j] << " ";
               }
              out << endl;
            }

        out << endl;
        out << "Kappa Statistics: " << Kappa << endl;
        out << "Total number of test instances:  " << countWhole << endl;
        out << endl;
        out << "=== Detailed accuracy by class ===" << endl;
        out << endl;
        for (size_t i = 0; i < pomClass.size(); i++) {
          out << "Class: " << QString::fromStdString(pomClass[i]) << "   TPrate: " << QString::number(TPrate[i], 'f', 2) << "   FP Rate: " << QString::number(FPrate[i], 'f', 2) << "   Precision: " << QString::number(Precision[i], 'f', 2) << "   Recall: " << QString::number(Recall[i], 'f', 2) << "   F-Measure: " << QString::number(Fmeasure[i], 'f', 2) << endl;
        }
        out << endl;
        out << "Elapsed time: " << nTime << " ms" << endl;
        out.flush();
     }

     fileTo.close();

     // Printing results from file to display
     QFile file(filename);
     QString line;
     ui->output->clear();
     ui->output->setLineWrapMode(QTextEdit::NoWrap);
     if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
            QTextStream stream(&file);
            while (!stream.atEnd()){
                line = stream.readLine();
                ui->output->setText(ui->output->toPlainText()+line+"\n");
            }
      }
      file.close();
}

/**
 * @brief The on_saveResults_clicked procedure saving results to file chosen by user
 */
void MainWindow::on_saveResults_clicked()
{
    // opens saving dialog for user
    QString fileName =
            QFileDialog::getSaveFileName(this, tr("Save File"),
                                         QDir::homePath(),
                                         tr("Text files (*.txt)"));

    // Saves file to user chosen directory
    QFile file(fileName);
    if (file.open(QIODevice::Append | QIODevice::Text)){
           QTextStream stream(&file);
           stream << ui->output->toPlainText() << endl;
    }
    file.close();

    QMessageBox msgBox;
    msgBox.setWindowTitle("Soubor uložen");
    msgBox.setText(" Soubor uložen do: "+fileName);
    msgBox.setIcon(QMessageBox::Information);
    msgBox.exec();
}

/**
 * @brief The copyAndOpenFile procedure opens help files
 */
void MainWindow::copyAndOpenFile(QString fileName){

    QFile file(fileName);

    //copying file from sources to project folder
    if (file.open(QIODevice::ReadOnly)){
        file.copy(fileName, "tmphelp.txt");
    }
    file.close();

    QDesktopServices::openUrl(QUrl("tmphelp.txt", QUrl::TolerantMode));
}

/**
 * @brief The on_actionGeneral_triggered procedure handling help
 */
void MainWindow::on_actionGeneral_triggered()
{
    QString fileName = ":/help/help/general.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionAbout_2_triggered procedure handling help
 */
void MainWindow::on_actionAbout_2_triggered()
{
    QString fileName = ":/help/help/about.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionNaive_Bayes_triggered procedure handling help
 */
void MainWindow::on_actionNaive_Bayes_triggered()
{
    QString fileName = ":/help/help/naivebayes.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionIB1_triggered procedure handling help
 */
void MainWindow::on_actionIB1_triggered()
{
    QString fileName = ":/help/help/ib1.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionIBk_triggered procedure handling help
 */
void MainWindow::on_actionIBk_triggered()
{
    QString fileName = ":/help/help/ib1.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionID3_triggered procedure handling help
 */
void MainWindow::on_actionID3_triggered()
{
    QString fileName = ":/help/help/ID3.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionRandom_Forest_triggered procedure handling help
 */
void MainWindow::on_actionRandom_Forest_triggered()
{
    QString fileName = ":/help/help/randomforest.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionUse_Training_Set_triggered procedure handling help
 */
void MainWindow::on_actionUse_Training_Set_triggered()
{
    QString fileName = ":/help/help/usetrainingset.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionCross_Validation_triggered procedure handling help
 */
void MainWindow::on_actionCross_Validation_triggered()
{
    QString fileName = ":/help/help/crossvalidation.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}

/**
 * @brief The on_actionPercentage_Split_triggered procedure handling help
 */
void MainWindow::on_actionPercentage_Split_triggered()
{
    QString fileName = ":/help/help/percentagesplit.txt";

    QFile fileToDestroy("tmphelp.txt");

    //removing previous file to copy new one with same name
    if (fileToDestroy.exists())
    {
        fileToDestroy.setPermissions(QFile::ReadOther | QFile::WriteOther);
        fileToDestroy.remove();
    }

    copyAndOpenFile(fileName);
}
