#include <iostream>
#include <fstream>
#include "pml.h"

using namespace std;

Pml::Pml(std::string nom_fichier) : tableau(), minMax(), nbVar()
{
    ifstream fichier(nom_fichier.c_str());

    if (fichier)
    {
        fichier >> minMax; // On récupère l'information : si c'est un minimum ou un maximum recherché

        int nbVariables = 0;

        fichier >> nbVariables; // on stock le nombre de variable
        nbVar = nbVariables;

        std::vector<float> fonction_economique;

        for (int i = 0; i < nbVariables; i++)
        {
            int tmp = 0;
            fichier >> tmp;

            if (minMax == -1)
            {
                tmp = -tmp; // si c'est un minimum on inverse la valeur des variables
            }

            fonction_economique.push_back(tmp);
        }

        int nbContraintes = 0;

        fichier >> nbContraintes; // on stock le nombre de contraintes

        tableau.resize((nbContraintes + 1), vector<float>(nbContraintes + nbVariables + 2)); //on definie la taille de notre tableau en fonction du nombre de contraintes récupéré, R+R'

        for (int i = 0; i < nbContraintes; i++) //Remplissage du tableau en fonction des contraintes et du signe
        {
            for (int j = 0; j < nbVariables; j++)
            {
                fichier >> tableau[i][j];
            }

            int signe = 0;

            fichier >> signe;

            for (int j = 0; j < nbContraintes; j++)//on remplie le tableau côté contraintes en fonction du signe (supérieur ou inférieur)
            {
                if (i == j)
                {
                    if (signe == -1 || signe == 0)
                    {
                        tableau[i][j + nbVariables] = 1;
                    }
                    else if (signe == 1)
                    {
                        tableau[i][j + nbVariables] = -1;
                    }
                }
                else
                {
                    tableau[i][j + nbVariables] = 0;
                }
            }

            fichier >> tableau[i][nbContraintes + nbVariables];
        }

        for (int i = 0; i < nbVariables; i++)
        {
            tableau[nbContraintes][i] = fonction_economique[i];
        }

        solve(); // on lance l'algorithme de résolution

        fichier.close();
    }
    else
    {
        cerr << "Erreur";
    }
}

bool Pml::checkOptimum()
{
    for (unsigned i = 0; i < tableau[0].size(); i++)
    {
        if (tableau[tableau.size() - 1][i] > 0) // si il reste sur la dernière ligne une valeur supérieur à zéro on renvoie faux et on continue
        {
            return false;
        }
    }

    return true;
}

unsigned Pml::vEntrante()
{
    int vEntrante = 0;

    for (unsigned i = 1; i < tableau[0].size(); i++) // on récupère la variable entrante en récupérant la plus grande valeur de la dernière ligne
    {
        if (tableau[tableau.size() - 1][i] > tableau[tableau.size() - 1][vEntrante])
        {
            vEntrante = i;
        }
    }

    return vEntrante;
}

unsigned Pml::vSortante()
{
    int indice = tableau[0].size() - 1;
    int vSortante = -1;

    for (unsigned i = 0; i < (tableau.size() - 1); i++) // on récupère la variable sortante en récupérant la plus petite valeur de R'
    {
        if (tableau[i][indice] > 0)
        {
            if (vSortante == -1)
            {
                vSortante = i;
            }
            else if (tableau[i][indice] < tableau[vSortante][indice])
            {
                vSortante = i;
            }
        }
    }

    return vSortante;
}

void Pml::remplirColonneR(unsigned ve)
{
    int indice_r = tableau[0].size() - 2;

    for (unsigned i = 0; i < tableau.size(); i++) // On remplit la colonne R'
    {
        tableau[i][indice_r + 1] = tableau[i][indice_r] / tableau[i][ve];
    }
}

void Pml::solve()
{
    while (checkOptimum() != true) // on verifie a chaque itération si l'optimum a été atteind, si il a été atteind on sort de la boucle et on enregistre les résultats
    {
        unsigned ve = vEntrante(); // l'aglorithme choisit la variable entrante

        remplirColonneR(ve); // on remplit la colonne R'

        unsigned vs = vSortante(); // l'aglorithme choisit la variable sortante

        std::vector<std::vector<float> > oldtableau = tableau; // on sauvegarde le tableau, pour pouvoir modifier l'acutelle tout en gardant les informations modifiées

        for (unsigned i = 0; i < tableau.size(); i++) // On remplit de 0 ou 1 la colonne de la variable entrante
        {
            if (i == vs)
            {
                tableau[i][ve] = 1;
            }
            else
            {
                tableau[i][ve] = 0;
            }
        }

        float coefficient = oldtableau[vs][ve];

        for (unsigned i = 0; i < (tableau[0].size() - 1); i++)
        {
            tableau[vs][i] = oldtableau[vs][i] / coefficient;
        }

        for (unsigned i = 0; i < tableau.size(); i++) // on réécrit toutes les lignes en fonction du pivot ve/vs
        {
            if (i != vs)
            {
                coefficient = oldtableau[i][ve];

                for (unsigned j = 0; j < (tableau[0].size() - 1); j++)
                {
                    tableau[i][j] = oldtableau[i][j] - (tableau[vs][j] * coefficient);
                }
            }
        }

        resultats(); // on écrit dans un fichier les résultats obtenus
    }
}

void Pml::resultats()
{
    ofstream fichier("resultats-PML.txt", ios::out | ios::trunc);
    float vOptimale = - tableau[tableau.size() - 1][tableau[0].size() - 2];

    fichier << vOptimale << "\n";
    fichier << nbVar << "\n";

    for (int i = 0; i < nbVar; i++)
    {
        bool trouve = false;

        for (int j = 0; j < (tableau.size() - 1); j++)
        {
            if (tableau[j][i] == 1)
            {
                fichier << tableau[j][tableau[0].size() - 2];
                fichier << "\n";
                trouve = true;
            }
        }

        if (trouve == false)
        {
            fichier << 0;
            fichier << "\n";
        }
    }
}
