#ifndef dimensionalityReduction_h
#define dimensionalityReduction_h

#include <limits>
#include <iostream>
#include <fstream>
#include <map>
#include <set>
#include <list>
#include <memory>
#include <algorithm>
#include <thread>

#include "lossFunctionMRP.h"



//************************************
//************************************
//************************************


class DimensionalityReductionResult {
public:
    std::uint_fast64_t& __all_le_elaborate;
    std::mutex& __all_le_elaborate_mutex;
private:
    std::mutex __le_elaborate_mutex;
    std::list<std::vector<std::uint_fast64_t>> __le_elaborate;
    std::list<double> __loss_values;
    std::list<std::vector<std::uint_fast64_t>>::iterator __best_permutation_iter;
    std::vector<std::uint_fast64_t> __best_permutation;
    double __best_loss;
    std::map<std::uint_fast64_t, std::tuple<std::uint_fast64_t, std::uint_fast64_t, double, double>> __best_profile_results;

public:
    DimensionalityReductionResult(std::uint_fast64_t&, std::mutex&);
    void AddLE(std::vector<std::uint_fast64_t> le, double loss);
    void AddLE(DimensionalityReductionResult&);
    std::list<std::vector<std::uint_fast64_t>>& LEElaborate();
    std::list<double>& LossValues();
    double BestLossValue();
    std::vector<std::uint_fast64_t>& BestLE();
    std::map<std::uint_fast64_t, std::tuple<std::uint_fast64_t, std::uint_fast64_t, double, double>>& BestProfileResults();

    void BuildBestProfileResults(std::shared_ptr<std::map<std::uint_fast64_t, double>>&,
                       LossFunctionMRPV2&,
                       std::vector<std::uint_fast64_t>&,
                       Matrice<double>&,
                       std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>&,
                       std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>&);


};

//************************************
//************************************
//************************************


void BidimentionalPosetRepresentation(std::uint_fast64_t,
                                      std::shared_ptr<std::map<std::uint_fast64_t, double>> ,
                                      std::string ,
                                      std::vector<std::uint_fast64_t>& ,
                                      DimensionalityReductionResult& result);

void ExactDimensionalityReductionSingleThread(std::uint_fast64_t,
                                              std::uint_fast64_t,
                                              std::uint_fast64_t,
                                              std::uint_fast64_t&,
                                              std::uint_fast64_t,
                                              LossFunctionMRPV2&,
                                              Matrice<double>&,
                                              std::shared_ptr<std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>>,
                                              std::shared_ptr<std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>>,
                                              std::vector<std::uint_fast64_t>&,
                                              std::shared_ptr<DimensionalityReductionResult>);

void ExactDimensionalityReductionThreads(std::shared_ptr<std::map<std::uint_fast64_t, double>>,
                                         std::uint_fast64_t,
                                         LossFunctionMRPV2&,
                                         std::vector<std::uint_fast64_t>&,
                                         std::shared_ptr<DisplayMessage>,
                                         double,
                                         DimensionalityReductionResult&);

void DimensionalityReductionBuildLE(std::uint_fast64_t,
                                    std::vector<std::uint_fast64_t>&,
                                    std::vector<std::uint_fast64_t>&,
                                    std::vector<std::uint_fast64_t>&,
                                    std::vector<std::uint_fast64_t>&,
                                    std::vector<std::uint_fast64_t>&);
void DimensionalityReductionBuildMRPIntersection(std::vector<std::uint_fast64_t>&,
                                                 std::vector<std::uint_fast64_t>&,
                                                 std::vector<std::uint_fast64_t>&,
                                                 std::vector<std::uint_fast64_t>&,
                                                 Matrice<double>&);

void DimensionalityReductionElaboraGruppoPermutazioni(std::uint_fast64_t,
                                                      std::uint_fast64_t,
                                                      std::uint_fast64_t,
                                                      std::vector<std::uint_fast64_t>&,
                                                      LossFunctionMRPV2&,
                                                      Matrice<double>&,
                                                      std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>&,
                                                      std::list<std::shared_ptr<std::vector<std::uint_fast64_t>>>&,
                                                      std::uint_fast64_t&,
                                                      std::shared_ptr<DimensionalityReductionResult>);


void DimensionalityReduction(std::uint_fast64_t,
                             std::shared_ptr<std::map<std::uint_fast64_t, double>>,
                             std::string,
                             std::shared_ptr<DisplayMessage>,
                             double,
                             DimensionalityReductionResult&);

double nimps(Matrice<double>& mrp_lvl,
             std::map<std::uint_fast64_t, double>& weights,
             std::map<std::uint_fast64_t, std::uint_fast64_t>& elements_conversion,
             std::map<std::uint_fast64_t, std::set<std::uint_fast64_t>>& elements_classes,
             Matrice<double>& mrp_out,
             std::map<std::uint_fast64_t, double>& weights_out,
             std::uint_fast64_t livello);

double nimps2(Matrice<double>& mrp_lvl,
             std::map<std::uint_fast64_t, double>& weights,
             std::map<std::uint_fast64_t, std::uint_fast64_t>& elements_conversion,
             std::map<std::uint_fast64_t, std::set<std::uint_fast64_t>>& elements_classes,
              Matrice<double>& mrp_out,
             std::map<std::uint_fast64_t, double>& weights_out,
             std::uint_fast64_t livello);


#endif /* dimensionalityReduction_h */
