#include <memory>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include <list>
#include <map>
#include <mutex>
#include <unordered_set>

#include "myException.h"
#include "types.h"
#include "random.h"
#include "matrice.h"
#include "displayMessage.h"
#include "linearGeneratorWrapper.h"
#include "posetWrapper.h"
#include "genericFunctions.h"
#include "separation.h"
#include "dimensionalityReduction.h"
#include "rSeparationUtils.h"
#include "rDisplayUtils.h"



template <class T>
static void chanFinalizer(SEXP ptr) {
    if(!R_ExternalPtrAddr(ptr)) return;
    T *x = static_cast<T*>(R_ExternalPtrAddr(ptr));
    delete x;
    R_ClearExternalPtr(ptr);
}

SEXP getListElement(SEXP list, const char *str)
{
    SEXP elmt = R_NilValue, names = Rf_getAttrib(list, R_NamesSymbol);
    auto len = Rf_length(names);
    for (long i = 0; i < len; i++)
        if(strcmp(R_CHAR(STRING_ELT(names, i)), str) == 0) {
           elmt = VECTOR_ELT(list, i);
           break;
        }
    return elmt;
}

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

SEXP Proteggi(SEXP s, int& conta) {
    ++conta;
    return PROTECT(s);
}

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

void forward_exception_to_r(const std::string message){
    int conta_proteggi = 0;
    auto stop_sym  = Proteggi(Rf_install("stop"), conta_proteggi) ;
    auto message_r = Proteggi(Rf_allocVector(STRSXP, 1), conta_proteggi);
    SET_STRING_ELT(message_r, 0, Rf_mkChar(message.c_str()));
    auto expr = Proteggi(Rf_lang2(stop_sym, message_r), conta_proteggi);
    Rf_eval(expr, R_GlobalEnv);
    if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
}

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

void forward_warning_to_r(const std::string message){
    int conta_proteggi = 0;
    auto warning_sym  = Proteggi(Rf_install("warning"), conta_proteggi) ;
    auto message_r = Proteggi(Rf_allocVector(STRSXP, 1), conta_proteggi);
    SET_STRING_ELT(message_r, 0, Rf_mkChar(message.c_str()));
    auto expr = Proteggi(Rf_lang2(warning_sym, message_r), conta_proteggi);
    Rf_eval(expr, R_GlobalEnv);
    if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
}


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

extern "C" {
    SEXP isReflexive(SEXP elements_r, SEXP comparabilities_r) {
        // isReflexive: check if the list of comparabilities represent a reflexive relation for the elements
        // parameters:
        //      elements: a string array containing elements without duplicate
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff for every element x in comparabilities the pair (x, x) is in comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements_r);
            std::vector<std::string> elements(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_r, k));
                elements.at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::map<std::string, std::set<std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                auto r = comparabilities.insert(std::make_pair(first, std::set<std::string>()));
                r.first->second.insert(second);
            }

            auto r = Generic::isReflexive(elements, comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;

        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP isSymmetric(SEXP comparabilities_r) {
        // isSymmetric: check if the list of comparabilities represent a symmetric relation
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff for every pair of elements (x, y) in comparabilities then (y, x) is in comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::list<std::pair<std::string, std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                comparabilities.push_back(std::pair<std::string, std::string>(first, second));
            }

            auto r = Generic::isSymmetric(comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;

        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP isAntisymmetric(SEXP comparabilities_r) {
        // isSymmetric: check if the list of comparabilities represent a symmetric relation
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff for every pair of elements (x, y) in comparabilities then (y, x) is in comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::list<std::pair<std::string, std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                comparabilities.push_back(std::pair<std::string, std::string>(first, second));
            }

            auto r = Generic::isAntisymmetric(comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;

        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP isTransitive(SEXP comparabilities_r) {
        // isTransitive: check if the list of comparabilities represent a tansitive relation
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff (x, y) and (y, z) in comparabilities imply (x, z) in comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::list<std::pair<std::string, std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                comparabilities.push_back(std::pair<std::string, std::string>(first, second));
            }

            auto r = Generic::isTransitive(comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP TransitiveClosure(SEXP comparabilities_r) {
        // TransitiveClosure: build the transitive clousure of comparabilities
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      a two columns character matrix containing the comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::list<std::pair<std::string, std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                comparabilities.push_back(std::pair<std::string, std::string>(first, second));
            }

            auto r = Generic::TransitiveClosure(comparabilities);
            result_r = Proteggi(Rf_allocMatrix(STRSXP, (int) r->size(), 2), conta_proteggi);
            std::uint_fast64_t pos = 0;
            for (auto p : *r) {
                auto p1 = p.first;
                auto p2 = p.second;
                SET_STRING_ELT(result_r, (long) (pos + r->size() * 0), Rf_mkChar(p1.c_str()));
                SET_STRING_ELT(result_r, (long) (pos + r->size() * 1), Rf_mkChar(p2.c_str()));
                pos++;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP ReflexiveClosure(SEXP elements_r, SEXP comparabilities_r) {
        // TransitiveClosure: build the transitive clousure of comparabilities
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      a two columns character matrix containing the comparabilities
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements_r);
            std::vector<std::string> elements(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_r, k));
                elements.at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::map<std::string, std::set<std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                auto r = comparabilities.insert(std::make_pair(first, std::set<std::string>()));
                r.first->second.insert(second);
            }

            auto r = Generic::ReflexiveClosure(elements, comparabilities);
            result_r = Proteggi(Rf_allocMatrix(STRSXP, (int) r->size(), 2), conta_proteggi);
            std::uint_fast64_t pos = 0;
            for (auto p : *r) {
                auto p1 = p.first;
                auto p2 = p.second;
                SET_STRING_ELT(result_r, (long) (pos + r->size() * 0), Rf_mkChar(p1.c_str()));
                SET_STRING_ELT(result_r, (long) (pos + r->size() * 1), Rf_mkChar(p2.c_str()));
                pos++;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP isPreorder(SEXP elements_r, SEXP comparabilities_r) {
        // isPreorder: check if the list of comparabilities represent a preorder relation in elements_r
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff comparabilities_r is reflexive and transitive
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements_r);
            std::vector<std::string> elements(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_r, k));
                elements.at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::map<std::string, std::set<std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                auto r = comparabilities.insert(std::make_pair(first, std::set<std::string>()));
                r.first->second.insert(second);
            }

            auto r = Generic::isPreorder(elements, comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP isPartialOrder(SEXP elements_r, SEXP comparabilities_r) {
        // isPartialOrder: check if the list of comparabilities represent a partial order relation
        // parameters:
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      true iff comparabilities_r is reflexive, antisymmetric and transitive
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements_r);
            std::vector<std::string> elements(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_r, k));
                elements.at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities_r, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            std::map<std::string, std::set<std::string>> comparabilities;
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities_r, k + nrow * 1));
                auto r = comparabilities.insert(std::make_pair(first, std::set<std::string>()));
                r.first->second.insert(second);
            }

            auto r = Generic::isPartialOrder(elements, comparabilities);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;

        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildPOSet(SEXP elements, SEXP comparabilities) {
        // BuildPoset: build a poset from elements and comparabilities
        // parameters:
        //      elements: a string array containing elements without duplicate
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements);
            auto poset_elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements, k));
                poset_elements->at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            auto poset_comparabilities = std::make_shared<std::list<std::pair<std::string, std::string>>>();
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities, k + nrow * 1));
                poset_comparabilities->push_back(std::pair<std::string, std::string>(first, second));
            }

            auto poset = POSetWrap::BuildGeneric(poset_elements, *poset_comparabilities);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildLinearPOSet(SEXP elements) {
        // BuildLinearPoset: build a total orderede poset from elements
        // parameters:
        //      elements: a string array containing elements without duplicate
        // return:
        //      S4 object of type LinearPoset. See BuildRObjectLinearPoset function for details
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements);
            auto poset_elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements, k));
                poset_elements->at((std::uint_fast64_t) k) = e;
            }
            auto poset_comparabilities = std::make_shared<std::list<std::pair<std::string, std::string>>>();

            auto poset = POSetWrap::BuildLinear(poset_elements);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildProductPOSet(SEXP posets_r) {
        // BuildProductPoset: build a ProductPoset from the list of posets_r
        // parameters:
        //      posets_r: list of ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long posets_len = Rf_length(posets_r);
            std::vector<POSetWrap*> posets(posets_len);
            for (long k = 0; k < posets_len; ++k) {
                POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(VECTOR_ELT(posets_r, k)));
                posets.at((std::uint_fast64_t) k) = poset;
            }
            auto poset = POSetWrap::BuildProduct(posets);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}


extern "C" {
    SEXP BuildLexicographicProductPOSet(SEXP posets_r) {
        // BuildLexicographicProductPOSet: build a LexicographicProductPOSet from the list of posets_r
        // parameters:
        //      posets_r: list of ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long posets_len = Rf_length(posets_r);
            std::vector<POSetWrap*> posets(posets_len);
            for (long k = 0; k < posets_len; ++k) {
                POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(VECTOR_ELT(posets_r, k)));
                posets.at((std::uint_fast64_t) k) = poset;
            }
            auto poset = POSetWrap::BuildLexicographicProduct(posets);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}


extern "C" {
    SEXP BuildIntersectionPOSet(SEXP posets_r) {
        // BuildIntersectionPOSet: build the poset obtained from the Intersectionof the list of posets_r
        // parameters:
        //      posets_r: list of ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long posets_len = Rf_length(posets_r);
            std::vector<POSetWrap*> posets(posets_len);
            for (long k = 0; k < posets_len; ++k) {
                POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(VECTOR_ELT(posets_r, k)));
                posets.at((std::uint_fast64_t) k) = poset;
            }
            auto poset = POSetWrap::BuildIntersection(posets);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildLinearSumPOSet(SEXP posets_r) {
        // BuildLinearSumPOSet: build the poset obtained from the Linear sum of the list of posets_r
        // parameters:
        //      posets_r: list of ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long posets_len = Rf_length(posets_r);
            std::vector<POSetWrap*> posets(posets_len);
            for (long k = 0; k < posets_len; ++k) {
                POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(VECTOR_ELT(posets_r, k)));
                posets.at((std::uint_fast64_t) k) = poset;
            }
            auto poset = POSetWrap::BuildLinearSum(posets);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildDisjointSumPOSet(SEXP posets_r) {
        // BuildDisjointSumPOSet: build the poset obtained from the Linear sum of the list of posets_r
        // parameters:
        //      posets_r: list of ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long posets_len = Rf_length(posets_r);
            std::vector<POSetWrap*> posets(posets_len);
            for (long k = 0; k < posets_len; ++k) {
                POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(VECTOR_ELT(posets_r, k)));
                posets.at((std::uint_fast64_t) k) = poset;
            }
            auto poset = POSetWrap::BuildDisjointSum(posets);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildLiftingPOSet(SEXP poset_r, SEXP new_element_r) {
        // BuildLiftingPOSet: build the poset obtained from poset adding plus new_element to bottom
        // parameters:
        //      poset_r: an ExternalPtrAddr of POSetWrap
        //      new_element_r: a string array containing a single element
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::string new_element = R_CHAR(STRING_ELT(new_element_r, 0));
            auto result = POSetWrap::BuildLiftingPOSet(poset, new_element);
            result_r = Proteggi(R_MakeExternalPtr(result, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildBucketPOSet(SEXP elements, SEXP comparabilities) {
        // BuildBucketPoset: build a BucketPoset from elements and comparabilities
        // parameters:
        //      elements: a string array containing elements without duplicate
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements);
            auto poset_elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements, k));
                poset_elements->at((std::uint_fast64_t) k) = e;
            }

            auto dim = Rf_getAttrib(comparabilities, R_DimSymbol);
            long nrow = INTEGER(dim)[0];

            auto poset_comparabilities = std::make_shared<std::list<std::pair<std::string, std::string>>>();
            for (long k = 0; k < nrow; ++k) {
                std::string first = R_CHAR(STRING_ELT(comparabilities, k + nrow * 0));
                std::string second = R_CHAR(STRING_ELT(comparabilities, k + nrow * 1));
                poset_comparabilities->push_back(std::pair<std::string, std::string>(first, second));
            }

            auto poset = POSetWrap::BuildBucketPOSet(poset_elements, *poset_comparabilities);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildBinaryVariablePOSet(SEXP variables) {
        // BuildBinaryVariablePOSet: build a BinaryVariablePOSet from variables
        // parameters:
        //      variables: a string array containing the name of the variables
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long variables_size = Rf_length(variables);
            std::vector<std::string> variables_names(variables_size);
            for (long k = 0; k < variables_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(variables, k));
                variables_names.at((std::uint_fast64_t) k) = e;
            }

            auto poset = POSetWrap::BuildBinaryVariablePOSet(variables_names);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildFencePOSet(SEXP elements_r, SEXP orientation_r) {
        // BuildPoset: build a fence from size
        // parameters:
        //      size_r: a string array containing elements without duplicate
        //      comparabilities: a two columns character matrix containing comparabilities
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_size = Rf_length(elements_r);
            auto poset_elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_r, k));
                poset_elements->at((std::uint_fast64_t) k) = e;
            }
            bool orientation = INTEGER(orientation_r)[0];
            auto poset = POSetWrap::BuildFencePOSet(poset_elements, orientation);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildCrownPOSet(SEXP elements_1_r, SEXP elements_2_r) {
        // BuildCrownPOSet: build a Crown from size
        //          elements_1_r = {a_1, ...., a_n}
        //          elements_2_r = {b_1, ...., b_n}
        //          for all i, j \in {1, ..., n},
        //              if i \neq j then a_i and a_j are incomparable
        //              if i \neq j then b_i and b_j are incomparable
        //              if i \neq j then a_i < b_j
        //              if i = j then a_i and b_j are incomparable
        // parameters:
        //      elements_1_r: a string array containing first group of elements without duplicate
        //      elements_2_r: a string array containing second group of elements without duplicate
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long elements_1_size = Rf_length(elements_1_r);
            auto poset_elements = std::make_shared<std::vector<std::string>>(2 * elements_1_size);
            auto elements_1 = std::make_shared<std::vector<std::string>>(elements_1_size);
            for (long k = 0; k < elements_1_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_1_r, k));
                elements_1->at((std::uint_fast64_t) k) = e;
                poset_elements->at((std::uint_fast64_t) k) = e;
            }

            auto elements_2 = std::make_shared<std::vector<std::string>>(elements_1_size);
            for (long k = 0; k < elements_1_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(elements_2_r, k));
                elements_2->at((std::uint_fast64_t) k) = e;
                poset_elements->at((std::uint_fast64_t) k + elements_1_size) = e;
            }

            auto poset_comparabilities = std::make_shared<std::list<std::pair<std::string, std::string>>>();
            for (long k = 0; k < elements_1_size; ++k) {
                for (long h = 0; h < elements_1_size; ++h) {
                    if (k != h) {
                        poset_comparabilities->push_back(std::pair<std::string, std::string>(elements_1->at(k), elements_2->at(h)));
                    }
                }
            }
            auto poset = POSetWrap::BuildGeneric(poset_elements, *poset_comparabilities);
            result_r = Proteggi(R_MakeExternalPtr(poset, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildDualPOSet(SEXP poset_r) {
        // BuildDualPOSet: build the poset obtained from poset
        // parameters:
        //      poset_r: an ExternalPtrAddr of POSetWrap
        // return:
        //      ExternalPtrAddr of POSetWrap
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto result = POSetWrap::BuildDualPOSet(poset);
            result_r = Proteggi(R_MakeExternalPtr(result, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<POSetWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Elements(SEXP poset_r) {
        // Elements: retrive poset elements
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a string array containig the poset elements.

        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto r = poset->Elements();
            result_r = Proteggi(Rf_allocVector(STRSXP, (int) r->size()), conta_proteggi);
            long riga = 0;
            for (auto v : *r) {
                SET_STRING_ELT(result_r, riga, Rf_mkChar(v.c_str()));
                ++riga;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IncidenceMatrix(SEXP poset_r) {
        // IncidenceMatrix: incidence matrix of the poset
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a NxN boolean matrix where rows and columns names are the posets elements
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto r1 = poset->IncidenceMatrix();
            auto r2 = poset->Elements();

            int nrow = (int) r1->size();
            int ncol = (int) r1->size();
            result_r = Proteggi(Rf_allocMatrix(LGLSXP, nrow, ncol), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, ncol), conta_proteggi);

            for (long riga = 0; riga < nrow; ++riga) {
                SET_VECTOR_ELT(result_r_names, riga, Rf_mkChar(r2->at((std::uint_fast64_t) riga).c_str()));
                for (long colonna = 0; colonna < ncol; ++colonna) {
                    LOGICAL(result_r)[riga + nrow * colonna] = r1->at((std::uint_fast64_t) riga).at((std::uint_fast64_t) colonna);
                }
            }
            auto dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
            SET_VECTOR_ELT(dimnames, 0, result_r_names);
            SET_VECTOR_ELT(dimnames, 1, result_r_names);
            Rf_setAttrib(result_r, R_DimNamesSymbol, dimnames);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}


extern "C" {
    SEXP OrderRelation(SEXP poset_r) {
        // OrderRelation: the order relation of the posets
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a two colums string matrix where [e1, e2] is a row of the matrix iff e1<=e2 in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto r = poset->OrderRelation();
            int nrow = (int) r->size();
            result_r = Proteggi(Rf_allocMatrix(STRSXP, nrow, 2), conta_proteggi);
            long riga = 0;
            for (auto v : *r) {
                SET_STRING_ELT(result_r, riga + nrow * 0, Rf_mkChar(v.first.c_str()));
                SET_STRING_ELT(result_r, riga + nrow * 1, Rf_mkChar(v.second.c_str()));
                ++riga;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IsDominatedBy(SEXP poset_r, SEXP v1, SEXP v2) {
        // IsDominatedBy: for all k in {0,...,length(v1))} check if v1[k]<=v2[k] in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      v1: a string array containing any poset_r elements
        //      v2: a string array containing any poset_r elements
        // comment: v1 and v2 must have the same length
        // return:
        //      a logical vector R with same length of v1 such that
        //      for all k in {0,...,length(v1))}, R[k] is TRUE if v1<=v2 in poset_r, R[k] is FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long size = Rf_length(v1);
            auto es1 = std::vector<std::string>((std::uint_fast64_t) size);
            auto es2 = std::vector<std::string>((std::uint_fast64_t) size);
            for (long k = 0; k < size; ++k) {
                std::string e1 = R_CHAR(STRING_ELT(v1, k));
                es1.at((std::uint_fast64_t) k) = e1;
                std::string e2 = R_CHAR(STRING_ELT(v2, k));
                es2.at((std::uint_fast64_t) k) = e2;
            }
            result_r = poset->IsDominatedBy(es1, es2, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Dominates(SEXP poset_r, SEXP v1, SEXP v2) {
        // Dominates: for all k in {0,...,length(v1))} check if v2[k]<=v1[k] in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      v1: a string array containing any poset_r elements
        //      v2: a string array containing any poset_r elements
        // comment: v1 and v2 must have the same length
        // return:
        //      a logical vector R with same length of v1 such that
        //      for all k in {0,...,length(v1))}, R[k] is TRUE if v2<=v1 in poset_r, R[k] is FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long size = Rf_length(v1);
            auto es1 = std::vector<std::string>((std::uint_fast64_t) size);
            auto es2 = std::vector<std::string>((std::uint_fast64_t) size);
            for (long k = 0; k < size; ++k) {
                std::string e1 = R_CHAR(STRING_ELT(v1, k));
                es1.at((std::uint_fast64_t) k) = e1;
                std::string e2 = R_CHAR(STRING_ELT(v2, k));
                es2.at((std::uint_fast64_t) k) = e2;
            }
            result_r = poset->Dominates(es1, es2, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP IsComparableWith(SEXP poset_r, SEXP v1, SEXP v2) {
        // IsComparableWith: for all k in {0,...,length(v1))} check either if v1[k]<=v2[k]  or v2[k]<=v1[k] in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      v1: a string array containing any poset_r elements
        //      v2: a string array containing any poset_r elements
        // comment: v1 and v2 must have the same length
        // return:
        //      a logical vector R with same length of v1 such that
        //      for all k in {0,...,length(v1))}, R[k] is TRUE if v1[k]<=v2[k] or v2[k]<=v1[k] in poset_r,
        //      R[k] is FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long size = Rf_length(v1);
            auto es1 = std::vector<std::string>((std::uint_fast64_t) size);
            auto es2 = std::vector<std::string>((std::uint_fast64_t) size);
            for (long k = 0; k < size; ++k) {
                std::string e1 = R_CHAR(STRING_ELT(v1, k));
                es1.at((std::uint_fast64_t) k) = e1;
                std::string e2 = R_CHAR(STRING_ELT(v2, k));
                es2.at((std::uint_fast64_t) k) = e2;
            }
            result_r = poset->IsComparableWith(es1, es2, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP IsIncomparableWith(SEXP poset_r, SEXP v1, SEXP v2) {
        // IsIncomparableWith: for all k in {0,...,length(v1))} check neither v1[k]<=v2[k] nor v2[k]<=v1[k] in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      v1: a string array containing any poset_r elements
        //      v2: a string array containing any poset_r elements
        // comment: v1 and v2 must have the same length
        // return:
        //      a logical vector R with same length of v1 such that
        //      for all k in {0,...,length(v1))}, R[k] is TRUE if neither v1[k]<=v2[k] nor v2[k]<=v1[k] in poset_r,
        //      R[k] is FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long size = Rf_length(v1);
            auto es1 = std::vector<std::string>((std::uint_fast64_t) size);
            auto es2 = std::vector<std::string>((std::uint_fast64_t) size);
            for (long k = 0; k < size; ++k) {
                std::string e1 = R_CHAR(STRING_ELT(v1, k));
                es1.at((std::uint_fast64_t) k) = e1;
                std::string e2 = R_CHAR(STRING_ELT(v2, k));
                es2.at((std::uint_fast64_t) k) = e2;
            }
            result_r = poset->IsIncomparableWith(es1, es2, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP UpsetOf(SEXP poset_r, SEXP insieme) {
        // UpsetOf: build the upset of insieme in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme: a string array containing any poset_r elements
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF there exists v in insieme such that v<=e in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long elements_size = Rf_length(insieme);
            auto elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme, k));
                elements->at((std::uint_fast64_t) k) = e;
            }
            auto r = poset->UpsetOf(elements);
            result_r = Proteggi(Rf_allocVector(STRSXP, (long) r->size()), conta_proteggi);
            long pos = 0;
            for (auto v : *r) {
                SET_STRING_ELT(result_r, pos++, Rf_mkChar(v.c_str()));
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IsUpset(SEXP poset_r, SEXP insieme) {
        // IsUpset: check if insieme is an upset in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme: a string array containing any poset_r elements
        // return:
        //      a logical vector R containing containing TRUE iff insieme is an upset in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long elements_size = Rf_length(insieme);
            auto elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme, k));
                elements->at((std::uint_fast64_t) k) = e;
            }
            auto r = poset->IsUpSet(elements);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IsDownset(SEXP poset_r, SEXP insieme) {
        // IsDownset: check if insieme is a downset in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme: a string array containing any poset_r elements
        // return:
        //      a logical vector R containing containing TRUE iff insieme is a downset in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long elements_size = Rf_length(insieme);
            auto elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme, k));
                elements->at((std::uint_fast64_t) k) = e;
            }
            auto r = poset->IsDownSet(elements);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP DownsetOf(SEXP poset_r, SEXP insieme) {
        // DownsetOf: build the downset of insieme in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme: a string array containing any poset_r elements
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF there exists v in insieme such that e<=v in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long elements_size = Rf_length(insieme);
            auto elements = std::make_shared<std::vector<std::string>>(elements_size);
            for (long k = 0; k < elements_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme, k));
                elements->at((std::uint_fast64_t) k) = e;
            }
            auto r = poset->DownsetOf(elements);
            result_r = Proteggi(Rf_allocVector(STRSXP, (long) r->size()), conta_proteggi);
            long pos = 0;
            for (auto v : *r) {
                SET_STRING_ELT(result_r, pos++, Rf_mkChar(v.c_str()));
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP ComparabilitySetOf(SEXP poset_r, SEXP elemento) {
        // ComparabilitySetOf: build the comparability set of elemento in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      elemento: a string array containing one poset_r element
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF  e<=elemento or elemento<=e in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::string e = R_CHAR(STRING_ELT(elemento, 0));
            result_r = poset->ComparabilitySetOf(e, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP IncomparabilitySetOf(SEXP poset_r, SEXP elemento) {
        // IncomparabilitySetOf: build the set of elements in poset_r that are not comparable with elemento
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      elemento: a string array containing one poset_r element
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF neither e<=elemento nor elemento<=e in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::string e = R_CHAR(STRING_ELT(elemento, 0));
            result_r = poset->IncomparabilitySetOf(e, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Maximal(SEXP poset_r) {
        // Maximal: build the set of maximal elementS in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF  e<v does not hold for every v in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->Maximal(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Minimal(SEXP poset_r) {
        // Minimal: build the set of minimal elements in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF  v<e does not hold for every v in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->Minimal(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP IsMaximal(SEXP poset_r, SEXP elemento) {
        // IsMaximal: check if elemento is a maximal element in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      elemento: a string array containing one poset_r element
        // return:
        //      a logical vector containing TRUE if for all element e in poset_r,
        //      elemento<e does not hold. The logical vector contains FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::string e = R_CHAR(STRING_ELT(elemento, 0));
            auto r = poset->IsMaximal(e);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
      } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
      } catch(...) {
          forward_exception_to_r("c++ exception (unknown reason)");
      }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IsMinimal(SEXP poset_r, SEXP elemento) {
        // IsMinimal: check if elemento is a minimal element in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      elemento: a string array containing one poset_r element
        // return:
        //      a logical vector containing TRUE if for all element e in poset_r,
        //      e<elemento does not hold. The logical vector contains FALSE otherwise
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::string e = R_CHAR(STRING_ELT(elemento, 0));
            auto r = poset->IsMinimal(e);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
      } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
      } catch(...) {
          forward_exception_to_r("c++ exception (unknown reason)");
      }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP Meet(SEXP poset_r, SEXP insieme_r) {
        // Meet: build the meet of insieme
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme_r: a string array containing poset_r elements
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF  e<v does not hold for every v in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long insieme_size = Rf_length(insieme_r);
            std::vector<std::string> insieme(insieme_size);
            for (long k = 0; k < insieme_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme_r, k));
                insieme.at((std::uint_fast64_t) k) = e;
            }
            result_r = poset->Meet(insieme, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Join(SEXP poset_r, SEXP insieme_r) {
        // Join: build the meet of insieme
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      insieme_r: a string array containing poset_r elements
        // return:
        //      a string vector R containing elements of poset_r such that
        //      for all element e in poset_r,
        //          e in R IFF  e<v does not hold for every v in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long insieme_size = Rf_length(insieme_r);
            std::vector<std::string> insieme(insieme_size);
            for (long k = 0; k < insieme_size; ++k) {
                std::string e = R_CHAR(STRING_ELT(insieme_r, k));
                insieme.at((std::uint_fast64_t) k) = e;
            }
            result_r = poset->Join(insieme, conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP CoverRelation(SEXP poset_r) {
        // CoverRelation: build the cover relation of poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a two columns string matrix R such that
        //      a row [e1, e2] is in R IFF e1<e2 in poset_r and for every e in poset_r
        //      e1<e and e<e2 do not hold
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->CoverRelation(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP CoverMatrix(SEXP poset_r) {
        // CoverMatrix: build the cover matrix of poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a NxN bolean matrix R where row and column names are all poser_r elements such that
        //      R[e1, e2] is TRUE IFF e1<e2 in poset_r and for every e in poset_r
        //      e1<e and e<e2 do not hold
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->CoverMatrix(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP Incomparabilities(SEXP poset_r) {
        // Incomparabilities: build all the incomparabilities pair in poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      a two columns string matrix R such that
        //      a row (e1, e2) is in R IFF neither e1<=e2 nor e2<=e1 in poset_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->Incomparabilities(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP IsExtensionOf(SEXP poset_r_1, SEXP poset_r_2) {
        // IsExtensionOf: check if poset_r_1 is an extention of poset_r_2
        // parameters:
        //      poset_r_1: ExternalPtrAddr of POSetWrap
        //      poset_r_2: ExternalPtrAddr of POSetWrap
        // return:
        //      a logical vector containing TRUE IFF for each pair (e1, e2) of elements
        //      e1<=e2 in poset_r_2 imply e1<=e2 in poset_r_1

        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset1 = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r_1));
            POSetWrap *poset2 = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r_2));
            auto r = poset1->IsExtensionOf(poset2);
            result_r = Proteggi(Rf_allocVector(LGLSXP, 1), conta_proteggi);
            LOGICAL(result_r)[0] = r;
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP BuildLEGenerator(SEXP poset_r) {
        // BuildLEGenerator: build a linear extention generator based on tree of ideals
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        // return:
        //      S4 object of type TreeOfIdealsGenerator.
        //      See BuildRObjectTreeOfIdealsGenerator function for details
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto generator = LinearGeneratorWrap::BuildLEGenerator(poset);
            result_r = Proteggi(R_MakeExternalPtr(generator, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<LinearGeneratorWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BuildBubleyDyerLEGenerator(SEXP poset_r, SEXP arg) {
        // BuildBubleyDyerLEGenerator: build a linear extention generator based on Bubley Dyer
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      arg: a named list of additional arguments. The list may contain:
        //              BubleyDyerSeed: a integer vector containing the seed value of RND generator
        // return:
        //      S4 object of type BubleyDyerGenerator.
        //      See BuildRObjectBubleyDyerGenerator function for details
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            auto arg_name = Rf_getAttrib(arg, R_NamesSymbol);
            long arg_size = Rf_length(arg_name);

            std::map<std::string, std::uint_fast64_t> arg_map;
            for (long k = 0; k < arg_size; ++k) {
                std::string n = CHAR(STRING_ELT(arg_name, k ));
                long v = INTEGER(VECTOR_ELT(arg, k))[0];
                arg_map[n] = (std::uint_fast64_t) v;
            }
            auto generator = LinearGeneratorWrap::BuildBubleyDyerGenerator(poset, arg_map);
            result_r = Proteggi(R_MakeExternalPtr(generator, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<LinearGeneratorWrap>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP LEGBubleyDyerGet(SEXP generator_r, SEXP from_start_r, SEXP quante_r, SEXP errore_r, SEXP output_ogni_in_sec_r) {
        // LEGBubleyDyerGet: return a list of LE from generator_r
        // parameters:
        //      generator_r: S4 object of type BubleyDyerGenerator
        //      from_start_r: a logical vector containing a boolean value
        //      quante_r: an integer vector containing a non negative number. Can be empty
        //      errore_r: an real vector containing a positive number. Can be empty
        //      output_ogni_in_sec_r: an integer vector containing a non negative number. Can be empty
        // return:
        //      a string matrix R containig in each columns a LE obtained from generator_r.
        //      the number of columns of R depend on the parameters quante_r and errore_r:
        //          if quante_r is not empty R columns number is exactly the value in quante_r.
        //          Otherwise (quante_r is empty) the number of columns is given by the following expression
        //                          N^4 * (log(N))^ 2 + N^3 * log(N) * log(errore^{-1});
        //          where N is the number of poset elements, log is the natural logarithm and
        //                   p is the value in errore_r (0 if empty)
        //      Parameter from_start_r equal true reset generator_r
        //      (the first LE is computed starting from the minimal elements)
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            LinearGeneratorWrap *generator = static_cast<LinearGeneratorWrap*>(R_ExternalPtrAddr(generator_r));
            bool from_start = LOGICAL(from_start_r)[0];
            long quante_size = Rf_length(quante_r);
            std::uint_fast64_t quante = std::numeric_limits<std::uint_fast64_t>::max();
            if (quante_size != 0) {
                quante = (std::uint_fast64_t) INTEGER(quante_r)[0];
            }
            long errore_size = Rf_length(errore_r);

            std::shared_ptr<double> errore = nullptr;
            if (errore_size != 0) {
                errore = std::make_shared<double>(REAL(errore_r)[0]);
            }

            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }
            auto r = generator->GetFromBubleyDyer(from_start, quante, errore, output_ogni_in_sec);
            int nrow = (int) generator->LESize();
            int ncol = (int) r->size();
            result_r = Proteggi(Rf_allocMatrix(STRSXP, nrow, ncol), conta_proteggi);
            long colonna = 0;
            for (auto v : *r) {
                for (long riga = 0; riga < nrow; ++riga) {
                    auto e = v->at((std::uint_fast64_t) riga);
                    SET_STRING_ELT(result_r, riga + nrow * colonna, Rf_mkChar(e.c_str()));
                }
                colonna++;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP LEGGet(SEXP generator_r, SEXP from_start_r, SEXP quante_r, SEXP output_ogni_in_sec_r) {
        // LEGGet: return a list of LE from generator_r
        // parameters:
        //      generator_r: S4 object of type TreeOfIdealsGenerator
        //      from_start_r: a logical vector containing a boolean value
        //      quante_r: an integer vector containing a non negative number. Can be empty
        //      output_ogni_in_sec_r: an integer vector containing a non negative number. Can be empty
        // return:
        //      a string matrix R containig in each columns a LE obtained from generator_r.
        //      the number of LE generated depend on parameter quante_r and on the state of generator_r:
        //          if quante_r is empty then R contains all the LE not already returned by generator_r
        //          Otherwise (quante_r is empty) R contains quante_r LE not already returned by generator_r
        //          up to the maximun number of LE available
        //      Parameter from_start_r equal true reset generator_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            LinearGeneratorWrap *generator = static_cast<LinearGeneratorWrap*>(R_ExternalPtrAddr(generator_r));
            bool from_start = LOGICAL(from_start_r)[0];
            long quante_size = Rf_length(quante_r);
            std::uint_fast64_t quante = std::numeric_limits<std::uint_fast64_t>::max();
            if (quante_size != 0) {
                quante = (std::uint_fast64_t) INTEGER(quante_r)[0];
            }
            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }

            auto r = generator->GetFromLE(from_start, quante, output_ogni_in_sec);
            int nrow = (int) generator->LESize();
            int ncol = (int) r->size();
            result_r = Proteggi(Rf_allocMatrix(STRSXP, nrow, ncol), conta_proteggi);
            long colonna = 0;
            for (auto v : *r) {
                for (long riga = 0; riga < nrow; ++riga) {
                    auto e = v->at((std::uint_fast64_t) riga);
                    SET_STRING_ELT(result_r, riga + nrow * colonna, Rf_mkChar(e.c_str()));
                }
                colonna++;
            }
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP ExactMRP(SEXP poset_r, SEXP output_ogni_in_sec_r) {
        // ExactMRP: build the mutual ranking probability matrix of poset_r
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      output_ogni_in_sec_r: an integer vector containing a non negative number. Can be empty
        // return:
        //      a NxN real matrix R where row and column names are all poser_r elements
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }
            result_r = poset->ExactMRP(output_ogni_in_sec, conta_proteggi);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP BuildBubleyDyerMRPGenerator(SEXP poset_r, SEXP seed_r) {
        // BuildBubleyDyerMRPGenerator: build a BubleyDyerMRPGenerator object
        // parameters:
        //      poset_r: ExternalPtrAddr of POSetWrap
        //      seed_r: an integer vector containing the seed value of RND generator
        // return:
        //      See BuildRObjectBubleyDyerGenerator function for details
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::uint_fast64_t seed_rnd = 0;
            if (Rf_length(seed_r) > 0) {
                seed_rnd = (std::uint_fast64_t) INTEGER(seed_r)[0];
            } else {
                seed_rnd = RandomUni::GENERATORE_SEED_RANDOM.RndNextInt(0, std::numeric_limits<std::uint_fast64_t>::max());
            }
            auto rnd = std::make_shared<RandomUni>(seed_rnd);
            auto object = BubleyDyerMRPGenerator::BuildBubleyDyerMRPGenerator(poset, rnd);
            result_r = Proteggi(R_MakeExternalPtr(object, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<BubleyDyerMRPGenerator>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP BubleyDyerMRP(SEXP bubley_dyer_r, SEXP quante_r, SEXP errore_r, SEXP output_ogni_in_sec_r) {
        // BuildBubleyDyerMRP: build the mutual ranking probability matrix of poset_r
        // parameters:
        //      bubley_dyer_r: an object of type BubleyDyer
        //      quante_r: an integer vector containing a non negative number. Can be empty
        //      errore_r: an integer vector containing a non negative number. Can be empty
        //      output_ogni_in_sec_r: an integer vector containing a non negative number. Can be empty
        // return:
        //      a NxN real matrix R where row and column names are all poser_r elements
        //      if old_mrp_r is empty then R is build from scrach otherwise the is build starting from
        //      old_mrp_r.
        //      See LEGBubleyDyerGet for parameter quante_r and errore_r
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            BubleyDyerMRPGenerator *bubley_dyer = static_cast<BubleyDyerMRPGenerator*>(R_ExternalPtrAddr(bubley_dyer_r));

            long quante_size = Rf_length(quante_r);
            std::uint_fast64_t quante = std::numeric_limits<std::uint_fast64_t>::max();
            if (quante_size != 0) {
                quante = (std::uint_fast64_t) INTEGER(quante_r)[0];
            }
            long errore_size = Rf_length(errore_r);
            std::shared_ptr<double> errore = nullptr;
            if (errore_size != 0) {
                errore = std::make_shared<double>(REAL(errore_r)[0]);
            }
            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }
            result_r = bubley_dyer->BuildMRP(quante, errore, output_ogni_in_sec, conta_proteggi);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP ExactEvaluation(SEXP poset_r, SEXP output_ogni_in_sec_r, SEXP functions_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));

            long functions_r_size = Rf_length(functions_r);
            std::vector<std::string> internal_functions((std::uint_fast64_t) functions_r_size);
            std::vector<SEXP> external_functions((std::uint_fast64_t) functions_r_size);
            for (long k = 0; k < functions_r_size; ++k) {
                internal_functions.at((std::uint_fast64_t) k) = "";
                external_functions.at((std::uint_fast64_t) k) = R_NilValue;
                auto function = VECTOR_ELT(functions_r, k);
                if (Rf_isString(function)) {
                    std::string function_name = R_CHAR(STRING_ELT(function, 0));
                    internal_functions.at((std::uint_fast64_t) k) = function_name;
                } else {
                    external_functions.at((std::uint_fast64_t) k) = function;
                    internal_functions.at((std::uint_fast64_t) k) = "RFunction";
                }
            }

            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }

            result_r = LinearGeneratorWrap::BuildExactEvaluation(poset,
                                                                 internal_functions,
                                                                 external_functions,
                                                                 output_ogni_in_sec,
                                                                 conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP BuildBubleyDyerEvaluationGenerator(SEXP poset_r, SEXP seed_r, SEXP functions_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::uint_fast64_t seed_rnd = 0;
            if (Rf_length(seed_r) > 0) {
                seed_rnd = (std::uint_fast64_t) INTEGER(seed_r)[0];
            } else {
                seed_rnd = RandomUni::GENERATORE_SEED_RANDOM.RndNextInt(0, std::numeric_limits<std::uint_fast64_t>::max());
            }
            auto rnd = std::make_shared<RandomUni>(seed_rnd);

            long functions_r_size = Rf_length(functions_r);
            std::vector<std::string> internal_functions((std::uint_fast64_t) functions_r_size);
            std::vector<SEXP> external_functions((std::uint_fast64_t) functions_r_size);
            for (long k = 0; k < functions_r_size; ++k) {
                internal_functions.at((std::uint_fast64_t) k) = "";
                external_functions.at((std::uint_fast64_t) k) = R_NilValue;
                auto function = VECTOR_ELT(functions_r, k);
                if (Rf_isString(function)) {
                    std::string function_name = R_CHAR(STRING_ELT(function, 0));
                    internal_functions.at((std::uint_fast64_t) k) = function_name;
                } else {
                    external_functions.at((std::uint_fast64_t) k) = function;
                    internal_functions.at((std::uint_fast64_t) k) = "RFunction";
                }
            }
            auto object = BubleyDyerEvaluationGenerator::BuildBubleyDyerEvaluationGenerator(poset, rnd, internal_functions, external_functions);
            result_r = Proteggi(R_MakeExternalPtr(object, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<BubleyDyerEvaluationGenerator>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP BubleyDyerEvaluation(SEXP generator_r, SEXP quante_r, SEXP errore_r, SEXP output_ogni_in_sec_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            BubleyDyerEvaluationGenerator *generator = static_cast<BubleyDyerEvaluationGenerator*>(R_ExternalPtrAddr(generator_r));
            long quante_size = Rf_length(quante_r);
            std::uint_fast64_t quante = std::numeric_limits<std::uint_fast64_t>::max();
            if (quante_size != 0) {
                quante = (std::uint_fast64_t) INTEGER(quante_r)[0];
            }

            long errore_size = Rf_length(errore_r);
            std::shared_ptr<double> errore = nullptr;
            if (errore_size != 0) {
                errore = std::make_shared<double>(REAL(errore_r)[0]);
            }

            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }

            result_r = generator->Evaluation(quante, errore, output_ogni_in_sec, conta_proteggi);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}


extern "C" {
    SEXP BruggemannLercheSorensenDominance(SEXP poset_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            result_r = poset->BruggemannLercheSorensenDominance(conta_proteggi);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP ExactSeparation(SEXP poset_r, SEXP output_ogni_in_sec_r, SEXP quali_r) {
        // ExactSeparation: ......
        // parameters:
        //      poset_r: .....
        //      output_ogni_in_sec_r: ....
        // return:
        //      ......
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }

            long quali_r_size = Rf_length(quali_r);
            std::vector<std::string> internal_functions(quali_r_size);
            std::vector<SEXP> external_functions(quali_r_size, R_NilValue);
            for (long k = 0; k < quali_r_size; ++k) {
                auto function = VECTOR_ELT(quali_r, k);
                std::string function_name = R_CHAR(STRING_ELT(function, 0));
                if (function_name == "asymmetricLower") {
                    internal_functions.at((std::uint_fast64_t) k) = "asymmetricLower";
                } else if (function_name == "asymmetricUpper") {
                    internal_functions.at((std::uint_fast64_t) k) = "asymmetricUpper";
                } else if (function_name == "symmetric") {
                    internal_functions.at((std::uint_fast64_t) k) = "symmetric";
                } else {
                  std::string err_str = "quale";
                  throw_line(err_str);
                }
            }

            result_r = LinearGeneratorWrap::BuildExactEvaluation(poset,
                                                                 internal_functions,
                                                                 external_functions,
                                                                 output_ogni_in_sec,
                                                                 conta_proteggi);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP RunLexSeparation(SEXP modalita_r) {
        // ExactSeparation: ......
        // parameters:
        //      ....
        // return:
        //      ......
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {

            std::unordered_set<std::uint_fast64_t> modalita_size;

            long modalita_r_size = Rf_length(modalita_r);
            std::vector<std::vector<std::string>> modalita_string(modalita_r_size, std::vector<std::string>());
            std::vector<std::uint_fast64_t> modalita(modalita_r_size, 0);
            for (long k = 0; k < modalita_r_size; ++k) {
                auto mod_r = VECTOR_ELT(modalita_r, k);
                long mod_r_size = Rf_length(mod_r);
                modalita_size.insert(mod_r_size);
                modalita_string.at(k) = std::vector<std::string>(mod_r_size, "");
                modalita.at(k) = (std::uint_fast64_t) mod_r_size;
                for (long h = 0; h < mod_r_size; ++h) {
                    std::string e = R_CHAR(STRING_ELT(mod_r, h));
                    modalita_string.at((std::uint_fast64_t) k).at((std::uint_fast64_t) h) = e;
                }
            }

            std::uint_fast64_t numero_variabili = (std::uint_fast64_t) modalita_r_size;

            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>>>
            > risultato;
            if (modalita_size.size() > 1) {
                risultato = LexSeparationDeg(modalita);
            } else {
                risultato = LexSeparationEqDeg(numero_variabili, *modalita_size.begin());
            }


            auto risultato_all = std::get<0>(risultato);
            auto risultato_lower = std::get<1>(risultato);
            auto risultato_upper = std::get<2>(risultato);
            auto risultato_vertical = std::get<3>(risultato);
            auto risultato_horizontal = std::get<4>(risultato);
            auto risultato_profili = std::get<5>(risultato);

            int risultato_nrow = (int) risultato_all->Rows();
            SEXP risultato_all_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);
            SEXP risultato_lower_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);
            SEXP risultato_upper_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);
            SEXP risultato_vertical_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);
            SEXP risultato_horizontal_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);

            for (int riga = 0; riga < risultato_nrow; ++riga) {
                for (int colonna = 0; colonna < risultato_nrow; ++colonna) {
                    REAL(risultato_all_r)[riga + risultato_nrow * colonna] = risultato_all->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    REAL(risultato_lower_r)[riga + risultato_nrow * colonna] = risultato_lower->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    REAL(risultato_upper_r)[riga + risultato_nrow * colonna] = risultato_upper->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    REAL(risultato_vertical_r)[riga + risultato_nrow * colonna] = risultato_vertical->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    REAL(risultato_horizontal_r)[riga + risultato_nrow * colonna] = risultato_horizontal->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                }
            }


            SEXP rows_names_r = Proteggi(Rf_allocVector(VECSXP, (int) risultato_nrow), conta_proteggi);
            SEXP cols_names_r = Proteggi(Rf_allocVector(VECSXP, (int) risultato_nrow), conta_proteggi);
            for (std::uint_fast64_t profilo_id = 0; profilo_id < ((std::uint_fast64_t) risultato_nrow); ++profilo_id) {
                std::string profilo_str = "";
                auto& profilo = risultato_profili->at(profilo_id);
                for (std::uint_fast64_t variable_id = 0; variable_id < numero_variabili; ++variable_id) {
                    auto mod_id = profilo.at(variable_id);
                    std::string mod = modalita_string.at(variable_id).at(mod_id);
                    profilo_str += (variable_id < numero_variabili - 1 ? mod + "_" : mod);
                }
                SET_VECTOR_ELT(rows_names_r, (long) profilo_id, Rf_mkChar(profilo_str.c_str()));
                SET_VECTOR_ELT(cols_names_r, (long) profilo_id, Rf_mkChar(profilo_str.c_str()));
            }
            SEXP names_r = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
            SET_VECTOR_ELT(names_r, 0, rows_names_r);
            SET_VECTOR_ELT(names_r, 1, cols_names_r);

            Rf_setAttrib(risultato_all_r, R_DimNamesSymbol, names_r);
            Rf_setAttrib(risultato_lower_r, R_DimNamesSymbol, names_r);
            Rf_setAttrib(risultato_upper_r, R_DimNamesSymbol, names_r);
            Rf_setAttrib(risultato_vertical_r, R_DimNamesSymbol, names_r);
            Rf_setAttrib(risultato_horizontal_r, R_DimNamesSymbol, names_r);

            result_r = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            SET_VECTOR_ELT(result_r, 0, risultato_all_r);
            SET_VECTOR_ELT(result_r_names, 0, Rf_mkChar("symmetric"));
            SET_VECTOR_ELT(result_r, 1, risultato_lower_r);
            SET_VECTOR_ELT(result_r_names, 1, Rf_mkChar("asymmetricLower"));
            SET_VECTOR_ELT(result_r, 2, risultato_upper_r);
            SET_VECTOR_ELT(result_r_names, 2, Rf_mkChar("asymmetricUpper"));
            SET_VECTOR_ELT(result_r, 3, risultato_vertical_r);
            SET_VECTOR_ELT(result_r_names, 3, Rf_mkChar("vertical"));
            SET_VECTOR_ELT(result_r, 4, risultato_horizontal_r);
            SET_VECTOR_ELT(result_r_names, 4, Rf_mkChar("horizontal"));
            Rf_setAttrib(result_r, R_NamesSymbol, result_r_names);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP RunLexMRP(SEXP modalita_r) {
        // RunLexMRP: ......
        // parameters:
        //      ....
        // return:
        //      ......
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            long modalita_r_size = Rf_length(modalita_r);
            std::vector<std::vector<std::string>> modalita_string(modalita_r_size, std::vector<std::string>());
            std::vector<std::uint_fast64_t> modalita(modalita_r_size, 0);
            for (long k = 0; k < modalita_r_size; ++k) {
                auto mod_r = VECTOR_ELT(modalita_r, k);
                long mod_r_size = Rf_length(mod_r);
                modalita_string.at(k) = std::vector<std::string>(mod_r_size, "");
                modalita.at(k) = (std::uint_fast64_t) mod_r_size;
                for (long h = 0; h < mod_r_size; ++h) {
                    std::string e = R_CHAR(STRING_ELT(mod_r, h));
                    modalita_string.at((std::uint_fast64_t) k).at((std::uint_fast64_t) h) = e;
                }
            }

            auto risultato = LexMRP(modalita);

            auto numero_variabili = modalita.size();
            auto risultato_mrp = std::get<0>(risultato);
            auto risultato_profili = std::get<1>(risultato);

            int risultato_nrow = (int) risultato_mrp->Rows();
            result_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_nrow, risultato_nrow), conta_proteggi);

            for (int riga = 0; riga < risultato_nrow; ++riga) {
                for (int colonna = 0; colonna < risultato_nrow; ++colonna) {
                    REAL(result_r)[riga + risultato_nrow * colonna] = risultato_mrp->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                }
            }

            SEXP rows_names_r = Proteggi(Rf_allocVector(VECSXP, (int) risultato_nrow), conta_proteggi);
            SEXP cols_names_r = Proteggi(Rf_allocVector(VECSXP, (int) risultato_nrow), conta_proteggi);
            for (std::uint_fast64_t profilo_id = 0; profilo_id < ((std::uint_fast64_t) risultato_nrow); ++profilo_id) {
                std::string profilo_str = "";
                auto& profilo = risultato_profili->at(profilo_id);
                for (std::uint_fast64_t variable_id = 0; variable_id < numero_variabili; ++variable_id) {
                    auto mod_id = profilo.at(variable_id);
                    std::string mod = modalita_string.at(variable_id).at(mod_id);
                    profilo_str += (variable_id < numero_variabili - 1 ? mod + "_" : mod);
                }
                SET_VECTOR_ELT(rows_names_r, (long) profilo_id, Rf_mkChar(profilo_str.c_str()));
                SET_VECTOR_ELT(cols_names_r, (long) profilo_id, Rf_mkChar(profilo_str.c_str()));
            }
            SEXP names_r = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
            SET_VECTOR_ELT(names_r, 0, rows_names_r);
            SET_VECTOR_ELT(names_r, 1, cols_names_r);

            Rf_setAttrib(result_r, R_DimNamesSymbol, names_r);

        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}



extern "C" {
    SEXP BuildBubleyDyerSeparationGenerator(SEXP poset_r, SEXP seed_r, SEXP quali_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            POSetWrap *poset = static_cast<POSetWrap*>(R_ExternalPtrAddr(poset_r));
            std::uint_fast64_t seed_rnd = 0;
            if (Rf_length(seed_r) > 0) {
                seed_rnd = (std::uint_fast64_t) INTEGER(seed_r)[0];
            } else {
                seed_rnd = RandomUni::GENERATORE_SEED_RANDOM.RndNextInt(0, std::numeric_limits<std::uint_fast64_t>::max());
            }
            auto rnd = std::make_shared<RandomUni>(seed_rnd);

            long quali_r_size = Rf_length(quali_r);
            std::vector<std::string> internal_functions(quali_r_size);
            std::vector<SEXP> external_functions(quali_r_size, R_NilValue);
            for (long k = 0; k < quali_r_size; ++k) {
                auto function = VECTOR_ELT(quali_r, k);
                std::string function_name = R_CHAR(STRING_ELT(function, 0));
                if (function_name == "asymmetricLower") {
                    internal_functions.at((std::uint_fast64_t) k) = "asymmetricLower";
                } else if (function_name == "asymmetricUpper") {
                    internal_functions.at((std::uint_fast64_t) k) = "asymmetricUpper";
                } else if (function_name == "symmetric") {
                    internal_functions.at((std::uint_fast64_t) k) = "symmetric";
                } else {
                  std::string err_str = "quale";
                  throw_line(err_str);
                }
            }

            auto object = BubleyDyerEvaluationGenerator::BuildBubleyDyerEvaluationGenerator(poset, rnd, internal_functions, external_functions);
            result_r = Proteggi(R_MakeExternalPtr(object, R_NilValue, R_NilValue), conta_proteggi);
            R_RegisterCFinalizerEx(result_r, chanFinalizer<BubleyDyerEvaluationGenerator>, TRUE);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP BubleyDyerSeparation(SEXP generator_r, SEXP quante_r, SEXP errore_r, SEXP output_ogni_in_sec_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            BubleyDyerEvaluationGenerator *generator = static_cast<BubleyDyerEvaluationGenerator*>(R_ExternalPtrAddr(generator_r));
            long quante_size = Rf_length(quante_r);
            std::uint_fast64_t quante = std::numeric_limits<std::uint_fast64_t>::max();
            if (quante_size != 0) {
                quante = (std::uint_fast64_t) INTEGER(quante_r)[0];
            }

            long errore_size = Rf_length(errore_r);
            std::shared_ptr<double> errore = nullptr;
            if (errore_size != 0) {
                errore = std::make_shared<double>(REAL(errore_r)[0]);
            }

            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::uint_fast64_t output_ogni_in_sec = std::numeric_limits<std::uint_fast64_t>::max();
            if (output_ogni_in_sec_size != 0) {
                output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
            }

            result_r = generator->Evaluation(quante, errore, output_ogni_in_sec, conta_proteggi);
        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}


extern "C" {
    SEXP FuzzySeparation(SEXP dominance_matrix_r, SEXP tnorm_r, SEXP tconorm_r, SEXP quali_r) {
        // FuzzySeparation: .............
        // parameters:
        //      dominance_matrix_r: ..........
        //      tnorm_r: ..........
        //      tconorm_r: ..........
        // return:
        //      .....
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {

            auto dominance_matrix_r_dim = Rf_getAttrib(dominance_matrix_r, R_DimSymbol);
            long dominance_matrix_r_nrow = INTEGER(dominance_matrix_r_dim)[0];

            auto dominance_matrix_r_names = Rf_getAttrib(dominance_matrix_r, R_DimNamesSymbol);
            auto dominance_matrix_r_row_names = VECTOR_ELT(dominance_matrix_r_names, 0);
            auto dominance_matrix_r_col_names = VECTOR_ELT(dominance_matrix_r_names, 1);

            auto dominance_matrix = std::make_shared<Matrice<double>>(dominance_matrix_r_nrow);

            for (long row = 0; row < dominance_matrix_r_nrow; ++row) {
                for (long col = 0; col < dominance_matrix_r_nrow; ++col) {
                    double valore = REAL(dominance_matrix_r)[row + dominance_matrix_r_nrow * col];
                    (*dominance_matrix)(row, col) = valore;
                }

            }
            std::shared_ptr<NormConorm> times = nullptr;
            std::shared_ptr<NormConorm> plus = nullptr;

            if (Rf_isString(tnorm_r)) {
                std::string tnorm_r_str = CHAR(STRING_ELT(tnorm_r, 0));
                if (tnorm_r_str == "minimum") {
                    times = std::make_shared<MinNormConorm>();
                    plus = std::make_shared<MaxNormConorm>();
                } else if (tnorm_r_str == "product") {
                    times = std::make_shared<ProdNormConorm>();
                    plus = std::make_shared<ProbNormConorm>();
                } else {
                  std::string err_str = "function";
                  throw_line(err_str);
                }
            } else {
                times = std::make_shared<FRNormConorm>(tnorm_r);
                plus = std::make_shared<FRNormConorm>(tconorm_r);
            }

            bool do_all = false;
            bool do_lower = false;
            bool do_upper = false;
            long quali_r_size = Rf_length(quali_r);
            for (long k = 0; k < quali_r_size; ++k) {
                auto quale = VECTOR_ELT(quali_r, k);
                std::string quale_name = R_CHAR(STRING_ELT(quale, 0));
                if (quale_name == "asymmetricLower") {
                    do_lower = true;
                } else if (quale_name == "asymmetricUpper") {
                    do_upper = true;
                } else if (quale_name == "symmetric") {
                    do_all = true;
                } else {
                  std::string err_str = "quale";
                  throw_line(err_str);
                }
            }

            auto risultato = GeneralSeparation(dominance_matrix, *times, *plus, do_all, do_lower, do_upper);


            auto risultato_all = std::get<0>(risultato);
            auto risultato_lower = std::get<1>(risultato);
            auto risultato_upper = std::get<2>(risultato);

            SEXP risultato_all_r = R_NilValue;
            SEXP risultato_lower_r = R_NilValue;
            SEXP risultato_upper_r = R_NilValue;

            if (risultato_all != nullptr) {
                int risultato_all_nrow = (int) risultato_all->Rows();
                risultato_all_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_all_nrow, risultato_all_nrow), conta_proteggi);

                for (int riga = 0; riga < risultato_all_nrow; ++riga) {
                    for (int colonna = 0; colonna < risultato_all_nrow; ++colonna) {
                        REAL(risultato_all_r)[riga + risultato_all_nrow * colonna] = risultato_all->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_all_r, R_DimNamesSymbol, dimnames);
            }
            if (risultato_lower != nullptr) {
                int risultato_lower_nrow = (int) risultato_lower->Rows();
                risultato_lower_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_lower_nrow, risultato_lower_nrow), conta_proteggi);

                for (int riga = 0; riga < risultato_lower_nrow; ++riga) {
                    for (int colonna = 0; colonna < risultato_lower_nrow; ++colonna) {
                        REAL(risultato_lower_r)[riga + risultato_lower_nrow * colonna] = risultato_lower->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_lower_r, R_DimNamesSymbol, dimnames);
            }
            if (risultato_upper != nullptr) {
                int risultato_upper_nrow = (int) risultato_upper->Rows();
                risultato_upper_r = Proteggi(Rf_allocMatrix(REALSXP, risultato_upper_nrow, risultato_upper_nrow), conta_proteggi);

                for (int riga = 0; riga < risultato_upper_nrow; ++riga) {
                    for (int colonna = 0; colonna < risultato_upper_nrow; ++colonna) {
                        REAL(risultato_upper_r)[riga + risultato_upper_nrow * colonna] = risultato_upper->at((std::uint_fast64_t) riga, (std::uint_fast64_t) colonna);
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_upper_r, R_DimNamesSymbol, dimnames);
            }

            result_r = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            SET_VECTOR_ELT(result_r, 0, risultato_all_r);
            SET_VECTOR_ELT(result_r_names, 0, Rf_mkChar("symmetric"));
            SET_VECTOR_ELT(result_r, 1, risultato_lower_r);
            SET_VECTOR_ELT(result_r_names, 1, Rf_mkChar("asymmetricLower"));
            SET_VECTOR_ELT(result_r, 2, risultato_upper_r);
            SET_VECTOR_ELT(result_r_names, 2, Rf_mkChar("asymmetricUpper"));
            Rf_setAttrib(result_r, R_NamesSymbol, result_r_names);
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP FuzzyInBetweenness(SEXP dominance_matrix_r, SEXP tnorm_r, SEXP tconorm_r, SEXP quali_r) {
        // FuzzyInBetweenness: .............
        // parameters:
        //      dominance_matrix_r: ..........
        //      tnorm_r: ..........
        //      tconorm_r: ..........
        // return:
        //      .....
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {

            auto dominance_matrix_r_dim = Rf_getAttrib(dominance_matrix_r, R_DimSymbol);
            int nrow = INTEGER(dominance_matrix_r_dim)[0];
            auto dominance_matrix_r_names = Rf_getAttrib(dominance_matrix_r, R_DimNamesSymbol);
            auto dominance_matrix_r_row_names = VECTOR_ELT(dominance_matrix_r_names, 0);
            auto dominance_matrix_r_col_names = VECTOR_ELT(dominance_matrix_r_names, 1);

            auto dominance = std::make_shared<Matrice<double>>(nrow);

            for (long row = 0; row < nrow; ++row) {
                for (long col = 0; col < nrow; ++col) {
                    double valore = REAL(dominance_matrix_r)[row + nrow * col];
                    (*dominance)(row, col) = valore;
                }

            }
            std::shared_ptr<NormConorm> times = nullptr;
            std::shared_ptr<NormConorm> plus = nullptr;

            if (Rf_isString(tnorm_r)) {
                std::string tnorm_r_str = CHAR(STRING_ELT(tnorm_r, 0));
                if (tnorm_r_str == "minimum") {
                    times = std::make_shared<MinNormConorm>();
                    plus = std::make_shared<MaxNormConorm>();
                } else if (tnorm_r_str == "product") {
                    times = std::make_shared<ProdNormConorm>();
                    plus = std::make_shared<ProbNormConorm>();
                } else {
                  std::string err_str = "function";
                  throw_line(err_str);
                }
            } else {
                times = std::make_shared<FRNormConorm>(tnorm_r);
                plus = std::make_shared<FRNormConorm>(tconorm_r);
            }

            bool do_all = false;
            bool do_lower = false;
            bool do_upper = false;
            std::shared_ptr<std::vector<std::vector<std::vector<double>>>> in_betweennes_lower = nullptr;
            std::shared_ptr<std::vector<std::vector<std::vector<double>>>> in_betweennes_upper = nullptr;
            std::shared_ptr<std::vector<std::vector<std::vector<double>>>> in_betweennes_all = nullptr;

            long quali_r_size = Rf_length(quali_r);
            for (long k = 0; k < quali_r_size; ++k) {
                auto quale = VECTOR_ELT(quali_r, k);
                std::string quale_name = R_CHAR(STRING_ELT(quale, 0));
                if (quale_name == "asymmetricLower") {
                    do_lower = true;
                    in_betweennes_lower = std::make_shared<std::vector<std::vector<std::vector<double>>>>(nrow, std::vector<std::vector<double>>(nrow, std::vector<double>(nrow)));
                } else if (quale_name == "asymmetricUpper") {
                    do_upper = true;
                    in_betweennes_upper = std::make_shared<std::vector<std::vector<std::vector<double>>>>(nrow, std::vector<std::vector<double>>(nrow, std::vector<double>(nrow)));
                } else if (quale_name == "symmetric") {
                    do_all = true;
                    in_betweennes_all = std::make_shared<std::vector<std::vector<std::vector<double>>>>(nrow, std::vector<std::vector<double>>(nrow, std::vector<double>(nrow)));
                } else {
                  std::string err_str = "quale";
                  throw_line(err_str);
                }
            }

            for (std::uint_fast64_t pi = 0; pi < ((std::uint_fast64_t) nrow); ++pi) {
                for (std::uint_fast64_t qi = pi + 1; qi < ((std::uint_fast64_t) nrow); ++qi) {
                    for (std::uint_fast64_t ri = 0; ri < ((std::uint_fast64_t) nrow); ++ri) {
                        double finb_prq;
                        double finb_qrp;
                        double finbqrp;
                        GeneralFuzzyInBetweenness(pi, qi, ri, dominance, *times, *plus, finb_prq, finb_qrp, finbqrp);
                        if (do_lower) {
                            in_betweennes_lower->at(pi).at(qi).at(ri) = finb_prq;
                            in_betweennes_lower->at(qi).at(pi).at(ri) = finb_qrp;
                        }
                        if (do_upper) {
                            in_betweennes_upper->at(pi).at(qi).at(ri) = finb_qrp;
                            in_betweennes_upper->at(qi).at(pi).at(ri) = finb_prq;
                        }
                        if (do_all) {
                            in_betweennes_all->at(pi).at(qi).at(ri) = finbqrp;
                            in_betweennes_all->at(qi).at(pi).at(ri) = finbqrp;
                        }
                    }
                }
            }

            SEXP risultato_all_r = R_NilValue;
            SEXP risultato_lower_r = R_NilValue;
            SEXP risultato_upper_r = R_NilValue;

            int depts = nrow;
            int rows = nrow;
            int cols = nrow;

            if (in_betweennes_all != nullptr) {
                risultato_all_r = Proteggi(Rf_alloc3DArray(REALSXP, rows, cols, depts), conta_proteggi);
                for (long d = 0; d < depts; ++d) {
                    for (long r = 0; r < rows; ++r) {
                        for (long c = 0; c < cols; ++c) {
                            REAL(risultato_all_r)[r + c * rows + cols * rows * d] = in_betweennes_all->at((std::uint_fast64_t) r).at((std::uint_fast64_t) c).at((std::uint_fast64_t) d);
                        }
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_all_r, R_DimNamesSymbol, dimnames);
            }
            if (in_betweennes_lower != nullptr) {
                risultato_lower_r = Proteggi(Rf_alloc3DArray(REALSXP, rows, cols, depts), conta_proteggi);
                for (long d = 0; d < depts; ++d) {
                    for (long r = 0; r < rows; ++r) {
                        for (long c = 0; c < cols; ++c) {
                            REAL(risultato_lower_r)[r + c * rows + cols * rows * d] = in_betweennes_lower->at((std::uint_fast64_t) r).at((std::uint_fast64_t) c).at((std::uint_fast64_t) d);
                        }
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_lower_r, R_DimNamesSymbol, dimnames);
            }
            if (in_betweennes_upper != nullptr) {
                risultato_upper_r = Proteggi(Rf_alloc3DArray(REALSXP, rows, cols, depts), conta_proteggi);
                for (long d = 0; d < depts; ++d) {
                    for (long r = 0; r < rows; ++r) {
                        for (long c = 0; c < cols; ++c) {
                            REAL(risultato_upper_r)[r + c * rows + cols * rows * d] = in_betweennes_upper->at((std::uint_fast64_t) r).at((std::uint_fast64_t) c).at((std::uint_fast64_t) d);
                        }
                    }
                }
                SEXP dimnames = Proteggi(Rf_allocVector(VECSXP, 2), conta_proteggi);
                SET_VECTOR_ELT(dimnames, 0, dominance_matrix_r_row_names);
                SET_VECTOR_ELT(dimnames, 1, dominance_matrix_r_col_names);
                Rf_setAttrib(risultato_lower_r, R_DimNamesSymbol, dimnames);
            }

            result_r = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            SET_VECTOR_ELT(result_r, 0, risultato_all_r);
            SET_VECTOR_ELT(result_r_names, 0, Rf_mkChar("symmetric"));
            SET_VECTOR_ELT(result_r, 1, risultato_lower_r);
            SET_VECTOR_ELT(result_r_names, 1, Rf_mkChar("asymmetricLower"));
            SET_VECTOR_ELT(result_r, 2, risultato_upper_r);
            SET_VECTOR_ELT(result_r_names, 2, Rf_mkChar("asymmetricUpper"));
            Rf_setAttrib(result_r, R_NamesSymbol, result_r_names);

            /*
            int depts = 4;
            int rows = 3;
            int cols = 2;
            result_r = Proteggi(Rf_alloc3DArray(REALSXP, rows, cols, depts), conta_proteggi);

            double conta = 1.0;
            for (long d = 0; d < depts; ++d) {
                for (long r = 0; r < rows; ++r) {
                    for (long c = 0; c < cols; ++c) {
                        REAL(result_r)[r + c * rows + cols * rows * d] = conta++;
                    }
                }
            }*/
        } catch(std::exception &ex) {
            forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return result_r;
    }
}

extern "C" {
    SEXP RunDimensionalityReduction(SEXP profile_r, SEXP weights_r, SEXP loss_r, SEXP output_ogni_in_sec_r, SEXP thread_percentage_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            // nessuna riga duplicata in weights_r
            auto profile_r_dim = Rf_getAttrib(profile_r, R_DimSymbol);
            long profile_r_nrow = INTEGER(profile_r_dim)[0];
            long profile_r_ncol = INTEGER(profile_r_dim)[1];

            //long weights_r_size = Rf_length(weights_r);
            // weights_r_size deve essere uguale a profile_r_nrow

            auto weights = std::make_shared<std::map<std::uint_fast64_t, double>>();

            for (long row = 0; row < profile_r_nrow; ++row) {
                std::uint_fast64_t valore = 0;
                for (long col = 0; col < profile_r_ncol; ++col) {
                    std::uint_fast64_t v = (std::uint_fast64_t) INTEGER(profile_r)[row + profile_r_nrow * col];
                    valore = valore * 2 + v;
                }
                (*weights)[valore] = REAL(weights_r)[row];
            }

            std::string loss_str = CHAR(STRING_ELT(loss_r, 0));


            std::uint64_t total_le = std::tgamma(profile_r_ncol - 1) * ((std::uint_fast64_t) (profile_r_ncol * (profile_r_ncol - 1)) / 2);
            std::uint_fast64_t all_le_count = 0;
            std::mutex all_le_count_mutex;

            long output_ogni_in_sec_size = Rf_length(output_ogni_in_sec_r);
            std::shared_ptr<DisplayMessage> displayMessage = nullptr;
            if (output_ogni_in_sec_size != 0) {
                auto output_ogni_in_sec = (std::uint_fast64_t) INTEGER(output_ogni_in_sec_r)[0];
                displayMessage = std::make_shared<DisplayMessageDimensionalityReductionR>(all_le_count, total_le, output_ogni_in_sec);

            } else {
                displayMessage = std::make_shared<DisplayMessageDimensionalityReductionR>(all_le_count, total_le, std::numeric_limits<std::uint_fast64_t>::max());
            }
            double thread_percentage = REAL(thread_percentage_r)[0];


            DimensionalityReductionResult result(all_le_count, all_le_count_mutex);
            DimensionalityReduction(profile_r_ncol, weights, loss_str, displayMessage, thread_percentage, result);

            auto le = result.LEElaborate();

            //SEXP r_linear_extention = Proteggi(Rf_allocMatrix(INTSXP, (int) le.size(), (int) numero_profili), conta_proteggi);
            SEXP r_linear_extention = Proteggi(Rf_allocMatrix(INTSXP, (int) le.size(), (int) profile_r_ncol), conta_proteggi);
            {
                std::uint_fast64_t riga = 0;
                for (auto& l : le) {
                    //for (std::uint_fast64_t colonna = 0; colonna < numero_profili; ++colonna) {
                    for (std::uint_fast64_t colonna = 0; colonna < ((std::uint_fast64_t) profile_r_ncol); ++colonna) {
                        auto v = l.at(colonna);
                        INTEGER(r_linear_extention)[riga + ((int) (le.size() * colonna))] = (int) (v + 1);
                    }
                    ++riga;
                }
            }
            auto loss = result.LossValues();
            SEXP r_loss = Proteggi(Rf_allocVector(REALSXP, (int) loss.size()), conta_proteggi);
            {
                std::uint_fast64_t k = 0;
                for (auto& l : loss) {
                    REAL(r_loss)[k] = l;
                    ++k;
                }
            }

            auto best_loss = result.BestLossValue();
            SEXP r_best_loss = Proteggi(Rf_allocVector(REALSXP, 1), conta_proteggi);
            {
                REAL(r_best_loss)[0] = best_loss;
            }

            auto best_le = result.BestLE();
            SEXP r_best_le = Proteggi(Rf_allocVector(REALSXP, (int) best_le.size()), conta_proteggi);
            {
                for (std::uint_fast64_t k = 0; k < best_le.size(); ++k) {
                    REAL(r_best_le)[k] = best_le.at(k) + 1;
                }
            }

            auto best_profile_results = result.BestProfileResults();
            auto r_best_profile_results = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            auto r_best_profile_results_names = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            {
                SEXP r_profiles = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_x_poss = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_y_poss = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_weights = Proteggi(Rf_allocVector(REALSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_error = Proteggi(Rf_allocVector(REALSXP, (int) best_profile_results.size()), conta_proteggi);
                {
                    std::uint_fast64_t k = 0;
                    for (auto& l : best_profile_results) {
                        INTEGER(r_profiles)[k] = (int) l.first;
                        INTEGER(r_x_poss)[k] = (int) std::get<0>(l.second);
                        INTEGER(r_y_poss)[k] = (int) std::get<1>(l.second);
                        REAL(r_weights)[k] = std::get<2>(l.second);
                        REAL(r_error)[k] = std::get<3>(l.second);
                        ++k;
                    }
                }
                SET_VECTOR_ELT(r_best_profile_results, 0, r_profiles);
                SET_VECTOR_ELT(r_best_profile_results_names, 0, Rf_mkChar("profiles"));
                SET_VECTOR_ELT(r_best_profile_results, 1, r_x_poss);
                SET_VECTOR_ELT(r_best_profile_results_names, 1, Rf_mkChar("x"));
                SET_VECTOR_ELT(r_best_profile_results, 2, r_y_poss);
                SET_VECTOR_ELT(r_best_profile_results_names, 2, Rf_mkChar("y"));
                SET_VECTOR_ELT(r_best_profile_results, 3, r_weights);
                SET_VECTOR_ELT(r_best_profile_results_names, 3, Rf_mkChar("weights"));
                SET_VECTOR_ELT(r_best_profile_results, 4, r_error);
                SET_VECTOR_ELT(r_best_profile_results_names, 4, Rf_mkChar("error"));
                Rf_setAttrib(r_best_profile_results, R_NamesSymbol, r_best_profile_results_names);
            }


            result_r = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            SET_VECTOR_ELT(result_r, 0, r_loss);
            SET_VECTOR_ELT(result_r_names, 0, Rf_mkChar("allLoss"));
            SET_VECTOR_ELT(result_r, 1, r_linear_extention);
            SET_VECTOR_ELT(result_r_names, 1, Rf_mkChar("variablesPriority"));
            SET_VECTOR_ELT(result_r, 2, r_best_loss);
            SET_VECTOR_ELT(result_r_names, 2, Rf_mkChar("bestLossValue"));
            SET_VECTOR_ELT(result_r, 3, r_best_le);
            SET_VECTOR_ELT(result_r_names, 3, Rf_mkChar("bestVariablesPriority"));
            SET_VECTOR_ELT(result_r, 4, r_best_profile_results);
            SET_VECTOR_ELT(result_r_names, 4, Rf_mkChar("bestRepresentation"));
            Rf_setAttrib(result_r, R_NamesSymbol, result_r_names);


        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}

extern "C" {
    SEXP RunBidimentionalPosetRepresentation(SEXP profile_r, SEXP weights_r, SEXP loss_r, SEXP variable_priority_r) {
        SEXP result_r = R_NilValue;
        int conta_proteggi = 0;
        try {
            // nessuna riga duplicata in weights_r
            auto profile_r_dim = Rf_getAttrib(profile_r, R_DimSymbol);
            long profile_r_nrow = INTEGER(profile_r_dim)[0];
            long profile_r_ncol = INTEGER(profile_r_dim)[1];

            //long weights_r_size = Rf_length(weights_r);
            // weights_r_size deve essere uguale a profile_r_nrow

            auto weights = std::make_shared<std::map<std::uint_fast64_t, double>>();

            for (long row = 0; row < profile_r_nrow; ++row) {
                std::uint_fast64_t valore = 0;
                for (long col = 0; col < profile_r_ncol; ++col) {
                    std::uint_fast64_t v = (std::uint_fast64_t) INTEGER(profile_r)[row + profile_r_nrow * col];
                    valore = valore * 2 + v;
                }
                (*weights)[valore] = REAL(weights_r)[row];
            }

            std::string loss_str = CHAR(STRING_ELT(loss_r, 0));

            auto variable_priority_r_dim = Rf_length(variable_priority_r);
            std::vector<std::uint_fast64_t> variable_priority(variable_priority_r_dim);
            for (long row = 0; row < variable_priority_r_dim; ++row) {
                variable_priority.at(row) = INTEGER(variable_priority_r)[row] - 1;
            }

            std::uint_fast64_t all_le_count = 0;
            std::mutex all_le_count_mutex;

            DimensionalityReductionResult result(all_le_count, all_le_count_mutex);
            BidimentionalPosetRepresentation(profile_r_ncol, weights, loss_str, variable_priority, result);

            auto best_loss = result.BestLossValue();
            SEXP r_best_loss = Proteggi(Rf_allocVector(REALSXP, 1), conta_proteggi);
            {
                REAL(r_best_loss)[0] = best_loss;
            }

            auto best_le = result.BestLE();
            SEXP r_best_le = Proteggi(Rf_allocVector(REALSXP, (int) best_le.size()), conta_proteggi);
            {
                for (std::uint_fast64_t k = 0; k < best_le.size(); ++k) {
                    REAL(r_best_le)[k] = (int) (best_le.at(k) + 1);
                }
            }

            auto best_profile_results = result.BestProfileResults();
            auto r_best_profile_results = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            auto r_best_profile_results_names = Proteggi(Rf_allocVector(VECSXP, 5), conta_proteggi);
            {
                SEXP r_profiles = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_x_poss = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_y_poss = Proteggi(Rf_allocVector(INTSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_weights = Proteggi(Rf_allocVector(REALSXP, (int) best_profile_results.size()), conta_proteggi);
                SEXP r_error = Proteggi(Rf_allocVector(REALSXP, (int) best_profile_results.size()), conta_proteggi);
                {
                    std::uint_fast64_t k = 0;
                    for (auto& l : best_profile_results) {
                        INTEGER(r_profiles)[k] = (int) l.first;
                        INTEGER(r_x_poss)[k] = (int) std::get<0>(l.second);
                        INTEGER(r_y_poss)[k] = (int) std::get<1>(l.second);
                        REAL(r_weights)[k] = std::get<2>(l.second);
                        REAL(r_error)[k] = std::get<3>(l.second);
                        ++k;
                    }
                }
                SET_VECTOR_ELT(r_best_profile_results, 0, r_profiles);
                SET_VECTOR_ELT(r_best_profile_results_names, 0, Rf_mkChar("profiles"));
                SET_VECTOR_ELT(r_best_profile_results, 1, r_x_poss);
                SET_VECTOR_ELT(r_best_profile_results_names, 1, Rf_mkChar("x"));
                SET_VECTOR_ELT(r_best_profile_results, 2, r_y_poss);
                SET_VECTOR_ELT(r_best_profile_results_names, 2, Rf_mkChar("y"));
                SET_VECTOR_ELT(r_best_profile_results, 3, r_weights);
                SET_VECTOR_ELT(r_best_profile_results_names, 3, Rf_mkChar("weights"));
                SET_VECTOR_ELT(r_best_profile_results, 4, r_error);
                SET_VECTOR_ELT(r_best_profile_results_names, 4, Rf_mkChar("error"));
                Rf_setAttrib(r_best_profile_results, R_NamesSymbol, r_best_profile_results_names);
            }

            result_r = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            auto result_r_names = Proteggi(Rf_allocVector(VECSXP, 3), conta_proteggi);
            SET_VECTOR_ELT(result_r, 0, r_best_loss);
            SET_VECTOR_ELT(result_r_names, 0, Rf_mkChar("lossValue"));
            SET_VECTOR_ELT(result_r, 1, r_best_le);
            SET_VECTOR_ELT(result_r_names, 1, Rf_mkChar("variablesPriority"));
            SET_VECTOR_ELT(result_r, 2, r_best_profile_results);
            SET_VECTOR_ELT(result_r_names, 2, Rf_mkChar("representation"));
            Rf_setAttrib(result_r, R_NamesSymbol, result_r_names);


        } catch(std::exception &ex) {
          forward_exception_to_r(ex.what());
        } catch(...) {
            forward_exception_to_r("c++ exception (unknown reason)");
        }
        if (conta_proteggi > 0) UNPROTECT(conta_proteggi);
        return (result_r);
    }
}




extern "C" {
  static const R_CallMethodDef CallEntries[] = {
      {"_isSymmetric", (DL_FUNC) &isSymmetric, 1},
      {"_isReflexive", (DL_FUNC) &isReflexive, 2},
      {"_isAntisymmetric", (DL_FUNC) &isAntisymmetric, 1},
      {"_isTransitive", (DL_FUNC) &isTransitive, 1},
      {"_isPreorder", (DL_FUNC) &isPreorder, 2},
      {"_isPartialOrder", (DL_FUNC) &isPartialOrder, 2},
      {"_TransitiveClosure", (DL_FUNC) &TransitiveClosure, 1},
      {"_ReflexiveClosure", (DL_FUNC) &ReflexiveClosure, 2},

      {"_BuildPoset", (DL_FUNC) &BuildPOSet, 2},
      {"_BuildLinearPoset", (DL_FUNC) &BuildLinearPOSet, 1},
      {"_BuildProductPoset", (DL_FUNC) &BuildProductPOSet, 1},
      {"_BuildLexicographicProductPOSet", (DL_FUNC) &BuildLexicographicProductPOSet, 1},
      {"_BuildBucketPoset", (DL_FUNC) &BuildBucketPOSet, 2},
      {"_BuildBinaryVariablePOSet", (DL_FUNC) &BuildBinaryVariablePOSet, 1},
      {"_BuildIntersectionPOSet", (DL_FUNC) &BuildIntersectionPOSet, 1},
      {"_BuildLinearSumPOSet", (DL_FUNC) &BuildLinearSumPOSet, 1},
      {"_BuildDisjointSumPOSet", (DL_FUNC) &BuildDisjointSumPOSet, 1},
      {"_BuildLiftingPOSet", (DL_FUNC) &BuildLiftingPOSet, 2},
      {"_BuildFencePOSet", (DL_FUNC) &BuildFencePOSet, 2},
      {"_BuildCrownPOSet", (DL_FUNC) &BuildCrownPOSet, 2},
      {"_BuildDualPOSet", (DL_FUNC) &BuildDualPOSet, 1},

      {"_Elements", (DL_FUNC) &Elements, 1},
      {"_IncidenceMatrix", (DL_FUNC) &IncidenceMatrix, 1},
      {"_OrderRelation", (DL_FUNC) &OrderRelation, 1},
      {"_CoverMatrix", (DL_FUNC) &CoverMatrix, 1},
      {"_CoverRelation", (DL_FUNC) &CoverRelation, 1},
      {"_IsDominatedBy", (DL_FUNC) &IsDominatedBy, 3},
      {"_Dominates", (DL_FUNC) &Dominates, 3},
      {"_IsComparableWith", (DL_FUNC) &IsComparableWith, 3},
      {"_IsIncomparableWith", (DL_FUNC) &IsIncomparableWith, 3},
      {"_UpsetOf", (DL_FUNC) &UpsetOf, 2},
      {"_IsUpset", (DL_FUNC) &IsUpset, 2},
      {"_DownsetOf", (DL_FUNC) &DownsetOf, 2},
      {"_IsDownset", (DL_FUNC) &IsDownset, 2},
      {"_ComparabilitySetOf", (DL_FUNC) &ComparabilitySetOf, 2},
      {"_IncomparabilitySetOf", (DL_FUNC) &IncomparabilitySetOf, 2},
      {"_Maximal", (DL_FUNC) &Maximal, 1},
      {"_Minimal", (DL_FUNC) &Minimal, 1},
      {"_IsMaximal", (DL_FUNC) &IsMaximal, 2},
      {"_IsMinimal", (DL_FUNC) &IsMinimal, 2},
      {"_Meet", (DL_FUNC) &Meet, 2},
      {"_Join", (DL_FUNC) &Join, 2},
      {"_Incomparabilities", (DL_FUNC) &Incomparabilities, 1},
      {"_IsExtensionOf", (DL_FUNC) &IsExtensionOf, 2},
      {"_BuildLEGenerator", (DL_FUNC) &BuildLEGenerator, 1},
      {"_BuildBubleyDyerLEGenerator", (DL_FUNC) &BuildBubleyDyerLEGenerator, 2},
      {"_LEGBubleyDyerGet", (DL_FUNC) &LEGBubleyDyerGet, 5},
      {"_LEGGet", (DL_FUNC) &LEGGet, 4},
      {"_ExactMRP", (DL_FUNC) &ExactMRP, 2},
      {"_BuildBubleyDyerMRPGenerator", (DL_FUNC) &BuildBubleyDyerMRPGenerator, 2},
      {"_BubleyDyerMRP", (DL_FUNC) &BubleyDyerMRP, 4},
      {"_ExactEvaluation", (DL_FUNC) &ExactEvaluation, 3},
      {"_BuildBubleyDyerEvaluationGenerator", (DL_FUNC) &BuildBubleyDyerEvaluationGenerator, 3},
      {"_BubleyDyerEvaluation", (DL_FUNC) &BubleyDyerEvaluation, 4},
      {"_BruggemannLercheSorensenDominance", (DL_FUNC) &BruggemannLercheSorensenDominance, 1},

      {"_FuzzySeparation", (DL_FUNC) &FuzzySeparation, 4},
      {"_FuzzyInBetweenness", (DL_FUNC) &FuzzyInBetweenness, 4},
      {"_ExactSeparation", (DL_FUNC) &ExactSeparation, 3},
      {"_LexSeparation", (DL_FUNC) &RunLexSeparation, 1},
      {"_LexMRP", (DL_FUNC) &RunLexMRP, 1},
      {"_BuildBubleyDyerSeparationGenerator", (DL_FUNC) &BuildBubleyDyerSeparationGenerator, 3},
      {"_BubleyDyerSeparation", (DL_FUNC) &BubleyDyerSeparation, 4},

      {"_RunDimensionalityReduction", (DL_FUNC) &RunDimensionalityReduction, 5},
      {"_RunBidimentionalPosetRepresentation", (DL_FUNC) &RunBidimentionalPosetRepresentation, 4},

      {NULL, NULL, 0}
  };

  void R_init_poseticDataAnalysis(DllInfo *dll) {
    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
    R_useDynamicSymbols(dll, FALSE);
  }
}
