#ifndef separation_hpp
#define separation_hpp

#include <memory>
#include <algorithm>
#include "matrice.h"

enum class GSCase {lower, upper, all};


class NormConorm {
protected:
    std::uint_fast64_t __calls;
public:
    NormConorm() {
        __calls = 0;
    }
    virtual ~NormConorm() {};
    virtual double operator()(double a, double b) = 0;
};

class MinNormConorm : public NormConorm {
public:
    MinNormConorm() : NormConorm() {
    }
    ~MinNormConorm() {};
    double operator()(double a, double b) {
        return std::min(a, b);
    };
};

class MaxNormConorm : public NormConorm {
public:
    MaxNormConorm() : NormConorm() {
    }
    ~MaxNormConorm() {};
    double operator()(double a, double b) {
        return std::max(a, b);
    };
};

class ProdNormConorm : public NormConorm {
public:
    ProdNormConorm() : NormConorm() {
    }
    ~ProdNormConorm() {};
    double operator()(double a, double b) {
        return a * b;
    };
};

class ProbNormConorm : public NormConorm {
public:
    ProbNormConorm() : NormConorm() {
    }
    ~ProbNormConorm() {};
    double operator()(double a, double b) {
        return a + b - a * b;
    };
};


void GeneralFuzzyInBetweenness(std::uint_fast64_t pi, std::uint_fast64_t qi, std::uint_fast64_t ri,
                                 std::shared_ptr<Matrice<double>> dominance,
                                 NormConorm& times,
                                 NormConorm& plus,
                       double& finb_prq, double& finb_qrp, double& finbqrp);
void CumulativeFuzzyInBetweenness(std::uint_fast64_t pi, std::uint_fast64_t qi,
                                 std::shared_ptr<Matrice<double>> dominance,
                                 NormConorm& times,
                                 NormConorm& plus,
                                 double& cfinbpq, double& cfinb_pq, double& cfinb_qp);

std::tuple<std::shared_ptr<Matrice<double>>, std::shared_ptr<Matrice<double>>, std::shared_ptr<Matrice<double>>>
GeneralSeparation(std::shared_ptr<Matrice<double>> dominance, NormConorm& times, NormConorm& plus, bool do_all, bool do_lower, bool do_upper);

std::tuple<
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<std::vector<std::vector<std::uint_fast64_t>>>
>
LexSeparationEqDeg(std::uint_fast64_t numero_variabili, std::uint_fast64_t numero_modalita);


std::tuple<
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<std::vector<std::vector<std::uint_fast64_t>>>
>
LexSeparationDeg(std::vector<std::uint_fast64_t>& numero_modalita);



std::tuple<
    std::shared_ptr<Matrice<double>>,
    std::shared_ptr<std::vector<std::vector<std::uint_fast64_t>>>
>
LexMRP(std::vector<std::uint_fast64_t>& numero_modalita);

#endif /* separation_hpp */
