#ifndef VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX
#define VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX
//#define NO_IMPORT_ARRAY

/*boost python before anything else*/
#include <boost/python.hpp>

/*std*/
#include <sstream>
#include <string>

/*vigra*/
#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/graphs.hxx>
#include <vigra/graph_maps.hxx>
#include <vigra/python_graph.hxx>
#include <vigra/graph_algorithms.hxx>
#include <vigra/metrics.hxx>
#include <vigra/multi_gridgraph.hxx>
#include <vigra/error.hxx>
#include <vigra/merge_graph_adaptor.hxx>
#include <vigra/hierarchical_clustering.hxx>
#include <vigra/timing.hxx>
namespace python = boost::python;

namespace vigra{

template<class GRAPH>
class LemonGraphHierachicalClusteringVisitor
:   public boost::python::def_visitor<LemonGraphHierachicalClusteringVisitor<GRAPH> >
{
public:

    friend class def_visitor_access;

    typedef GRAPH Graph;
    typedef MergeGraphAdaptor<Graph> MergeGraph;

    typedef LemonGraphHierachicalClusteringVisitor<GRAPH> VisitorType;
    // Lemon Graph Typedefs

    typedef typename Graph::index_type       index_type;
    typedef typename Graph::Edge             Edge;
    typedef typename Graph::Node             Node;
    typedef typename Graph::Arc              Arc;
    typedef typename Graph::NodeIt           NodeIt;
    typedef typename Graph::EdgeIt           EdgeIt;
    typedef typename Graph::ArcIt            ArcIt;

    typedef EdgeHolder<Graph> PyEdge;
    typedef NodeHolder<Graph> PyNode;
    typedef  ArcHolder<Graph> PyArc;

    // predefined array (for map usage)
    const static unsigned int EdgeMapDim = IntrinsicGraphShape<Graph>::IntrinsicEdgeMapDimension;
    const static unsigned int NodeMapDim = IntrinsicGraphShape<Graph>::IntrinsicNodeMapDimension;

    typedef NumpyArray<EdgeMapDim,   Singleband<float > > FloatEdgeArray;
    typedef NumpyArray<NodeMapDim,   Singleband<float > > FloatNodeArray;
    typedef NumpyArray<NodeMapDim,   Singleband<UInt32> > UInt32NodeArray;
    typedef NumpyArray<NodeMapDim,   Singleband<Int32 > > Int32NodeArray;
    typedef NumpyArray<NodeMapDim +1,Multiband <float > > MultiFloatNodeArray;

    typedef NumpyScalarEdgeMap<Graph,FloatEdgeArray>         FloatEdgeArrayMap;
    typedef NumpyScalarNodeMap<Graph,FloatNodeArray>         FloatNodeArrayMap;
    typedef NumpyScalarNodeMap<Graph,UInt32NodeArray>        UInt32NodeArrayMap;
    typedef NumpyScalarNodeMap<Graph,Int32NodeArray>         Int32NodeArrayMap;
    typedef NumpyMultibandNodeMap<Graph,MultiFloatNodeArray> MultiFloatNodeArrayMap;

    typedef cluster_operators::EdgeWeightNodeFeatures<
        MergeGraph,
        FloatEdgeArrayMap,
        FloatEdgeArrayMap,
        MultiFloatNodeArrayMap,
        FloatNodeArrayMap,
        FloatEdgeArrayMap,
        UInt32NodeArrayMap
    > DefaultClusterOperator;

    //typedef cluster_operators::EdgeWeightNodeFeatures2<
    //    MergeGraph,
    //    FloatEdgeArrayMap,
    //    FloatEdgeArrayMap,
    //    MultiFloatNodeArrayMap,
    //    FloatNodeArrayMap,
    //    FloatEdgeArrayMap
    //> NeuroClusterOperator;

    typedef cluster_operators::PythonOperator<MergeGraph> PythonClusterOperator;

    LemonGraphHierachicalClusteringVisitor(const std::string clsName)
    :clsName_(clsName){

    }

    void exportMergeGraph()const{
        const std::string mgAdaptorClsName = clsName_ + std::string("MergeGraph");
        python::class_<MergeGraph,boost::noncopyable>(
            mgAdaptorClsName.c_str(),python::init<const Graph &>()[python::with_custodian_and_ward<1 /*custodian == self*/, 2 /*ward == const InputLabelingView & */>()]
        )
        .def(LemonUndirectedGraphCoreVisitor<MergeGraph>(mgAdaptorClsName))
        .def("inactiveEdgesNode",&pyInactiveEdgesNode)
        .def("graph",&pyMergeGraphsGraph, python::return_internal_reference<>())
        .def("contractEdge",&pyContractEdgeA)
        .def("contractEdge",&pyContractEdgeB)
        .def("hasEdgeId",&pyHasEdgeId)

        .def("graphLabels",registerConverters(&pyCurrentLabeling<MergeGraph>),
            (
                python::arg("out")=python::object()
            )
        )

        ;

        python::def("__mergeGraph",&pyMergeGraphConstructor ,
            python::with_custodian_and_ward_postcall< 0,1 ,
                    python::return_value_policy<   python::manage_new_object      >  >()
        )
        ;
    }

    static void setLiftedEdges(DefaultClusterOperator & op,
        NumpyArray<1, uint32_t> liftedEdgeIds
    ){
        op.setLiftedEdges(liftedEdgeIds.begin(), liftedEdgeIds.end());
    }

    void exportHierarchicalClusteringOperators()const{
        {
            const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("MinEdgeWeightNodeDistOperator");
            python::class_<DefaultClusterOperator  >(operatorName.c_str(),python::no_init)
            .def("__init__", python::make_constructor(&pyEdgeWeightNodeFeaturesConstructor))
            .def("setLiftedEdges",registerConverters(&setLiftedEdges))
            .def("enableStopWeight",&DefaultClusterOperator::enableStopWeight)
            ;
            python::def("__minEdgeWeightNodeDistOperator",registerConverters(&pyEdgeWeightNodeFeaturesConstructor),
                python::with_custodian_and_ward_postcall< 0,1 ,
                    python::with_custodian_and_ward_postcall< 0 ,2,
                        python::with_custodian_and_ward_postcall< 0 ,3,
                            python::with_custodian_and_ward_postcall< 0 ,4,
                                python::with_custodian_and_ward_postcall< 0 ,5,
                                    python::with_custodian_and_ward_postcall< 0 ,6,
                                        python::with_custodian_and_ward_postcall< 0 ,7,
                                            python::return_value_policy<   python::manage_new_object
                >  >    >   >   >   >   >   >()
            );

        }
        //{
        //    const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("NeuroOperator");
        //    python::class_<NeuroClusterOperator  >(operatorName.c_str(),python::no_init)
        //    .def("__init__", python::make_constructor(&pyNeuroConstructor))
        //    ;
        //    python::def("__neuroOperator",registerConverters(&pyNeuroConstructor),
        //        python::with_custodian_and_ward_postcall< 0,1 ,
        //            python::with_custodian_and_ward_postcall< 0 ,2,
        //                python::with_custodian_and_ward_postcall< 0 ,3,
        //                    python::with_custodian_and_ward_postcall< 0 ,4,
        //                        python::with_custodian_and_ward_postcall< 0 ,5,
        //                            python::with_custodian_and_ward_postcall< 0 ,6,
        //                                python::return_value_policy<   python::manage_new_object
        //        >  >    >   >   >   >   >()
        //    );
        //}
        {

            const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("PythonOperator");
            python::class_<PythonClusterOperator  >(operatorName.c_str(),python::no_init)
            .def("__init__", python::make_constructor(&pyPythonOperatorConstructor))//,
                //python::return_value_policy<python::manage_new_object>() )
            ;
            python::def("__pythonClusterOperator",registerConverters(&pyPythonOperatorConstructor),
                python::with_custodian_and_ward_postcall< 0,1 ,
                    python::with_custodian_and_ward_postcall< 0,2 ,
                        python::return_value_policy<   python::manage_new_object      >  >  >()
            );
        }
    }

    template<class CLUSTER_OPERATOR>
    void exportHierarchicalClustering(const std::string & opClsName)const{
        typedef CLUSTER_OPERATOR ClusterOperator;
        typedef HierarchicalClusteringImpl<ClusterOperator> HCluster;

        const std::string clsName = std::string("HierarchicalClustering")+ opClsName;
        python::class_<HCluster,boost::noncopyable>(
            clsName.c_str(),python::init<ClusterOperator &>()[python::with_custodian_and_ward<1 /*custodian == self*/, 2 /*ward == const InputLabelingView & */>()]
        )
        .def("cluster",&HCluster::cluster)
        .def("reprNodeIds",registerConverters(&pyReprNodeIds<HCluster>))
        .def("ucmTransform",registerConverters(&pyUcmTransform<HCluster>))
        .def("resultLabels",registerConverters(&pyResultLabels<HCluster>),
            (
                python::arg("out")=python::object()
            )
        )
        ;

        // free function
        python::def("__hierarchicalClustering",registerConverters(&pyHierarchicalClusteringConstructor<ClusterOperator>),
            python::with_custodian_and_ward_postcall< 0,1 ,
                    python::return_value_policy<   python::manage_new_object      >  >()
        );
    }

    static const Graph & pyMergeGraphsGraph(const MergeGraph & mg){
        return mg.graph();
    }

    static MergeGraph * pyMergeGraphConstructor(const GRAPH & graph){
        return new MergeGraphAdaptor<GRAPH>(graph);
    }

    template<class MG>
    static NumpyAnyArray pyCurrentLabeling(
        const MG  & mergeGraph,
        UInt32NodeArray   resultArray
    ){
        resultArray.reshapeIfEmpty(IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(mergeGraph.graph()));

        UInt32NodeArrayMap resultArrayMap(mergeGraph.graph(),resultArray);
        //std::cout<<"find result labels\n";

        //USETICTOC;

        //TIC;
        for(NodeIt iter(mergeGraph.graph());iter!=lemon::INVALID;++iter ){
            resultArrayMap[*iter]=mergeGraph.reprNodeId(mergeGraph.graph().id(*iter));
        }
        //TOC;
        return resultArray;
    }

    template <class classT>
    void visit(classT& /*c*/) const
    {
        // the merge graph itself and factory functions to get a merge graph
        exportMergeGraph();

        // export the clustering operators
        exportHierarchicalClusteringOperators();

        // export Hierarchical Clustering (for all  cluster operators)
        //{
        //    const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("NeuroOperator");
        //    exportHierarchicalClustering<NeuroClusterOperator>(operatorName);
        //}
        {
            const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("MinEdgeWeightNodeDistOperator");
            exportHierarchicalClustering<DefaultClusterOperator>(operatorName);
        }
        {
            const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("PythonOperator");
            exportHierarchicalClustering<PythonClusterOperator>(operatorName);
        }

    }

    static NodeHolder<MergeGraph> pyInactiveEdgesNode(
        const MergeGraph & mg,
        const EdgeHolder<MergeGraph> & edge
    ){
        return NodeHolder<MergeGraph>(mg,mg.inactiveEdgesNode(edge));
    }

    static EdgeHolder<MergeGraph> pyReprEdgeID(
        const MergeGraph & mg,
        const EdgeHolder<MergeGraph> & edge
    ){
        return EdgeHolder<MergeGraph>(mg,mg.reprEdge(edge));
    }

    static void pyContractEdgeA(
        MergeGraph & mg,
        const EdgeHolder<MergeGraph> & edge
    ){
        mg.contractEdge(edge);
    }

    static void pyContractEdgeB(
        MergeGraph & mg,
        const EdgeHolder<Graph> & graphEdge
    ){
        mg.contractEdge(mg.reprEdge(graphEdge));
    }

    static bool pyHasEdgeId(
        MergeGraph & mg,
        typename MergeGraph::index_type id
    ){
        return mg.hasEdgeId(id);
    }

    template<class CLUSTER_OP>
    static HierarchicalClusteringImpl<CLUSTER_OP> * pyHierarchicalClusteringConstructor(
        CLUSTER_OP & clusterOp,
        const size_t nodeNumStopCond,
        const bool buildMergeTreeEncoding

    ){
        typename HierarchicalClusteringImpl<CLUSTER_OP>::Parameter param;
        param.nodeNumStopCond_=nodeNumStopCond;
        param.buildMergeTreeEncoding_=buildMergeTreeEncoding;
        param.verbose_=true;
        return new  HierarchicalClusteringImpl<CLUSTER_OP>(clusterOp,param);
    }

    static DefaultClusterOperator *
    pyEdgeWeightNodeFeaturesConstructor(
        MergeGraph &                mergeGraph,
        FloatEdgeArray              edgeIndicatorMapArray,
        FloatEdgeArray              edgeSizeMapArray,
        MultiFloatNodeArray         nodeFeatureMapArray,
        FloatNodeArray              nodeSizeMapArray,
        FloatEdgeArray              edgeMinWeightMapArray,
        UInt32NodeArray             nodeLabelArray,
        const float                 beta,
        const metrics::MetricType   nodeDistType,
        const float                 wardness,
        const float                 gamma
    ){

        FloatEdgeArrayMap       edgeIndicatorMap(mergeGraph.graph(),edgeIndicatorMapArray);
        FloatEdgeArrayMap       edgeSizeMap(mergeGraph.graph(),edgeSizeMapArray);
        MultiFloatNodeArrayMap  nodeFeatureMap(mergeGraph.graph(),nodeFeatureMapArray);
        FloatNodeArrayMap       nodeSizeMap(mergeGraph.graph(),nodeSizeMapArray);
        FloatEdgeArrayMap       edgeMinWeightMap(mergeGraph.graph(),edgeMinWeightMapArray);
        UInt32NodeArrayMap      nodeLabelMap(mergeGraph.graph(),nodeLabelArray);

        return new DefaultClusterOperator(mergeGraph,
            edgeIndicatorMap,edgeSizeMap,
            nodeFeatureMap, nodeSizeMap,
            edgeMinWeightMap,nodeLabelMap,
            beta,nodeDistType,wardness, gamma
        );
    }

    static PythonClusterOperator *
    pyPythonOperatorConstructor(
        MergeGraph & mergeGraph,
        python::object object,
        const bool useMergeNodeCallback,
        const bool useMergeEdgesCallback,
        const bool useEraseEdgeCallback
    ){
        return new PythonClusterOperator(mergeGraph,object,useMergeNodeCallback,useMergeEdgesCallback,useEraseEdgeCallback);
    }

    template<class HCLUSTER>
    static void pyReprNodeIds(
        const HCLUSTER &     hcluster,
        NumpyArray<1,UInt32> labels
    ){
        for(MultiArrayIndex i=0; i<labels.shape(0); ++i)
            labels(i)=hcluster.reprNodeId(labels(i));
    }

    template<class HCLUSTER>
    static NumpyAnyArray pyResultLabels(
        const HCLUSTER  & hcluster,
        UInt32NodeArray   resultArray
    ){
        resultArray.reshapeIfEmpty(IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(hcluster.graph()));

        UInt32NodeArrayMap resultArrayMap(hcluster.graph(),resultArray);
        //std::cout<<"find result labels\n";

        //USETICTOC;

        //TIC;
        for(NodeIt iter(hcluster.graph());iter!=lemon::INVALID;++iter ){
            resultArrayMap[*iter]=hcluster.mergeGraph().reprNodeId(hcluster.graph().id(*iter));
        }
        //TOC;
        return resultArray;
    }

    template<class HCLUSTER>
    static void pyUcmTransform(
        const HCLUSTER  & hcluster,
        FloatEdgeArray  inputArray
    ){
        FloatEdgeArrayMap inputArrayMap(hcluster.graph(),inputArray);
        hcluster.ucmTransform(inputArrayMap);
    }

    template<class HCLUSTER>
    static python::tuple mergeTreeEncodingAsNumpyArray(const HCLUSTER & hcluster) {
        typedef typename HCLUSTER::MergeTreeEncoding      MergeTreeEncoding;
        typedef typename HCLUSTER::MergeGraphIndexType    MergeGraphIndexType;
        typedef typename HCLUSTER::ValueType              ValueType;
        const MergeTreeEncoding & encoding = hcluster.mergeTreeEndcoding();
        const MergeGraphIndexType numMerges = encoding.size();
        //CPP BUG?!?
        NumpyArray<1,ValueType> w = NumpyArray<1,ValueType>(typename NumpyArray<1,ValueType>::difference_type(numMerges));
        NumpyArray<2,MergeGraphIndexType> indices = NumpyArray<2,MergeGraphIndexType>(typename NumpyArray<2,MergeGraphIndexType>::difference_type(numMerges,3));
        for(MergeGraphIndexType m=0;m<numMerges;++m){
            w(int(m))=encoding[m].w_;
            indices(m,0)=encoding[m].a_;
            indices(m,1)=encoding[m].b_;
            indices(m,2)=encoding[m].r_;
        }
        return python::make_tuple(indices,w);
    }

    template<class HCLUSTER>
    static python::tuple leafNodeIdsAsNumpyArray(
        const HCLUSTER &            hcluster,
        const typename HCLUSTER::MergeGraphIndexType treeNodeId,
        NumpyArray<1,UInt32>  leafes  = (NumpyArray<1,UInt32>())
    ) {
        leafes.reshapeIfEmpty( typename NumpyArray<1,UInt32>::difference_type( hcluster.graph().nodeNum()) );
        if(leafes.shape(0)!=hcluster.graph().nodeNum()){
            throw std::runtime_error("out.shape(0) must be equal nodeNum");
        }

        // todo make leafes size check
        const size_t leafNum=hcluster.leafNodeIds(treeNodeId,leafes.begin());
        return python::make_tuple(leafes,leafNum);
    }

    /*

    template<class T>
    static NumpyAnyArray pyRagProjectNodeFeaturesToBaseGraph(
        const RagGraph &                                         rag,
        const Graph    &                                         graph,
        const typename PyNodeMapTraits<Graph,   UInt32>::Array & labelsWhichGeneratedRagArray,
        const typename PyNodeMapTraits<RagGraph,T     >::Array & ragNodeFeaturesArray,
        const Int32                                              ignoreLabel=-1,
        typename PyNodeMapTraits<Graph,T>::Array                 graphNodeFeaturesArray=typename PyNodeMapTraits<Graph,T>::Array() // out
    ){
        // reshape out  ( last argument (out) will be reshaped if empty, and #channels is taken from second argument)
        reshapeNodeMapIfEmpty(graph,ragNodeFeaturesArray,graphNodeFeaturesArray);
        // numpy arrays => lemon maps
        typename PyNodeMapTraits<Graph,   UInt32>::Map labelsWhichGeneratedRagArrayMap(graph, labelsWhichGeneratedRagArray);
        typename PyNodeMapTraits<RagGraph,T     >::Map ragNodeFeaturesArrayMap(rag,ragNodeFeaturesArray);
        typename PyNodeMapTraits<Graph,   T     >::Map graphNodeFeaturesArrayMap(graph,graphNodeFeaturesArray);
        // run algorithm
        for(typename Graph::NodeIt iter(graph);iter!=lemon::INVALID;++iter){
            if(ignoreLabel==-1 || static_cast<Int32>(labelsWhichGeneratedRagArrayMap[*iter])!=ignoreLabel)
                graphNodeFeaturesArrayMap[*iter]=ragNodeFeaturesArrayMap[rag.nodeFromId(labelsWhichGeneratedRagArrayMap[*iter])];
            else{
                // ???
                // aks U. Koethe here
            }
        }
        return graphNodeFeaturesArray; // out
    }
    */
private:
    std::string clsName_;
};

} // end namespace vigra

#endif // VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX


#include "drake/math/barycentric.h"

#include <algorithm>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include <Eigen/Dense>

#include "drake/common/drake_assert.h"

namespace drake {
namespace math {

template <typename T>
BarycentricMesh<T>::BarycentricMesh(MeshGrid input_grid)
    : input_grid_(std::move(input_grid)),
      stride_(input_grid_.size()),
      num_interpolants_{1} {
  DRAKE_DEMAND(input_grid_.size() > 0);
  for (int i = 0; i < get_input_size(); i++) {
    // Must define at least one mesh point per dimension.
    DRAKE_DEMAND(!input_grid_[i].empty());

    // Gain one interpolant for every non-singleton dimension.
    if (input_grid_[i].size() > 1) num_interpolants_++;

    stride_[i] = (i == 0) ? 1 : input_grid_[i - 1].size() * stride_[i - 1];
  }
}

template <typename T>
void BarycentricMesh<T>::get_mesh_point(int index,
                                        EigenPtr<Eigen::VectorXd> point) const {
  DRAKE_DEMAND(index >= 0);
  DRAKE_DEMAND(point != nullptr);
  point->resize(get_input_size());
  // Iterate through the input dimensions, assigning the value and reducing
  // the index to be relevant only to the remaining dimensions.
  for (int i = 0; i < get_input_size(); i++) {
    const auto& coords = input_grid_[i];
    const int dim_index = index % coords.size();
    (*point)[i] = *(std::next(input_grid_[i].begin(), dim_index));
    index /= coords.size();  // intentionally truncate to int.
  }
  DRAKE_DEMAND(index == 0);  // otherwise the index was out of range.
}

template <typename T>
VectorX<T> BarycentricMesh<T>::get_mesh_point(int index) const {
  VectorX<T> point(get_input_size());
  get_mesh_point(index, &point);
  return point;
}

template <typename T>
MatrixX<T> BarycentricMesh<T>::get_all_mesh_points() const {
  const int M = get_input_size();
  const int N = get_num_mesh_points();
  VectorX<T> point(M);
  MatrixX<T> points(M, N);
  for (int i = 0; i < N; i++) {
    get_mesh_point(i, &point);
    points.col(i) = point;
  }
  return points;
}

template <typename T>
void BarycentricMesh<T>::EvalBarycentricWeights(
    const Eigen::Ref<const VectorX<T>>& input,
    EigenPtr<Eigen::VectorXi> mesh_indices,
    EigenPtr<VectorX<T>> weights) const {
  DRAKE_DEMAND(input.size() == static_cast<int>(input_grid_.size()));
  DRAKE_DEMAND(mesh_indices != nullptr && weights != nullptr);

  // std::pair of fractional position [0,1] and dimension index (position first,
  // so that std::pair's default operator< works for us).
  // There is one relative position for every non-singleton input dimension.  In
  // the case of triangular meshes, there is one interpolant for every
  // non-singular dimension + one additional, so num_interpolants-1 is the size
  // we need.
  std::vector<std::pair<T, int>> relative_position(num_interpolants_ - 1);
  // Bounding box on the input grid containing the sample input.
  std::vector<bool> has_volume(get_input_size());

  int current_index = 0;

  // Loop through input dimensions and compute the relative positions and
  // indices.  Set current_index to the "top right" corner index.
  int count = 0;
  for (int i = 0; i < get_input_size(); i++) {
    const auto& coords = input_grid_[i];

    // Skip over singleton dimensions.
    if (coords.size() == 1) continue;

    // Tag the positions with the dimension index.
    relative_position[count].second = i;

    // Find the right side of the bounding box.
    // Recall that lower_bound returns the first grid element that is NOT less
    // than the sample.
    auto right_iter = coords.lower_bound(input[i]);
    int right_index = 0;

    if (right_iter == coords.end()) {
      // ... then input is off the right end of the grid;
      // move it to the right boundary.
      has_volume[i] = false;
      right_index = coords.size() - 1;
      relative_position[count].first = T(1.);
    } else if (right_iter == coords.begin()) {
      // ... then input is at the first element or left of it;
      // move it to the left boundary.
      has_volume[i] = false;
      right_index = 0;
      relative_position[count].first = T(1.);
    } else {
      // ... then input is inside the grid.
      has_volume[i] = true;
      right_index = std::distance(coords.begin(), right_iter);
      const T& right_value = *(right_iter);
      const T& left_value = *(std::prev(right_iter));
      relative_position[count].first =
          (input[i] - left_value) / (right_value - left_value);
    }

    current_index += stride_[i] * right_index;
    count++;
  }
  DRAKE_ASSERT(count == (num_interpolants_ - 1));

  // Sort the dimensions by their relative position.  We identify which triangle
  // of the mesh we are in by moving along the faces in order of their relative
  // position.
  std::sort(relative_position.begin(), relative_position.end());

  mesh_indices->resize(num_interpolants_);
  weights->resize(num_interpolants_);
  (*mesh_indices)[0] = current_index;
  (*weights)[0] = relative_position[0].first;

  for (int i = 1; i < num_interpolants_; i++) {
    int dim = relative_position[i - 1].second;
    if (has_volume[dim]) {
      current_index -= stride_[dim];
    }
    (*mesh_indices)[i] = current_index;
    if (i == (num_interpolants_ - 1)) {
      (*weights)[i] = 1.0 - relative_position[i - 1].first;
    } else {
      (*weights)[i] =
          relative_position[i].first - relative_position[i - 1].first;
    }
  }
}

template <typename T>
void BarycentricMesh<T>::Eval(const Eigen::Ref<const MatrixX<T>>& mesh_values,
                              const Eigen::Ref<const VectorX<T>>& input,
                              EigenPtr<VectorX<T>> output) const {
  EvalWithMixedScalars<T>(mesh_values, input, output);
}

template <typename T>
VectorX<T> BarycentricMesh<T>::Eval(
    const Eigen::Ref<const MatrixX<T>>& mesh_values,
    const Eigen::Ref<const VectorX<T>>& input) const {
  return EvalWithMixedScalars<T>(mesh_values, input);
}

template <typename T>
MatrixX<T> BarycentricMesh<T>::MeshValuesFrom(
    const std::function<VectorX<T>(const Eigen::Ref<const VectorX<T>>&)>&
        vector_func) const {
  VectorX<T> sample(get_input_size());
  const int N = get_num_mesh_points();

  // Call it once to determine the size of the output and initialize memory.
  get_mesh_point(0, &sample);
  VectorX<T> value = vector_func(sample);
  MatrixX<T> mesh_values(value.rows(), N);
  mesh_values.col(0) = value;

  // Now loop through all of the other points.
  for (int i = 1; i < N; i++) {
    get_mesh_point(i, &sample);
    mesh_values.col(i) = vector_func(sample);
  }

  return mesh_values;
}

}  // namespace math
}  // namespace drake

template class ::drake::math::BarycentricMesh<double>;


/**
 * @date Tue Apr 26 19:20:57 2011 +0200
 * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
 * @author Tiago Freitas Pereira <Tiago.Pereira@idiap.ch>
 * @author Andre Anjos <andre.anjos@idiap.ch>
 *
 * This class can be used to calculate the LBP-Top  of a set of image frames
 * representing a video sequence (c.f. Dynamic Texture Recognition Using Local
 * Binary Patterns with an Application to Facial Expression from Zhao &
 * Pietikäinen, IEEE Trans. on PAMI, 2007). This is the implementation file.
 *
 * Copyright (C) Idiap Research Institute, Martigny, Switzerland
 */

#include <stdexcept>
#include <boost/format.hpp>
#include <bob.ip.base/LBPTop.h>

bob::ip::base::LBPTop::LBPTop(
    boost::shared_ptr<LBP> lbp_xy,
    boost::shared_ptr<LBP> lbp_xt,
    boost::shared_ptr<LBP> lbp_yt
):
  m_lbp_xy(lbp_xy),
  m_lbp_xt(lbp_xt),
  m_lbp_yt(lbp_yt)
{
  /*
   * Checking the inputs. The radius in XY,XT and YT must be the same
   * Note: radii are in opposity direction lbp_xy has radii stored in (y,x), aso.
   */
  if(lbp_xy->getRadii()[1]!=lbp_xt->getRadii()[1]) {
    throw std::runtime_error((boost::format("The X radii of R_xy (%f) and R_xt (%f) do not match")% lbp_xy->getRadii()[1] % lbp_xt->getRadii()[1]).str());
  }

  if(lbp_xy->getRadii()[0]!=lbp_yt->getRadii()[1]) {
    throw std::runtime_error((boost::format("The Y radii R_xy (%f) and R_yt (%f) do not match")% lbp_xy->getRadii()[0] % lbp_yt->getRadii()[1]).str());
  }

  if(lbp_xt->getRadii()[0]!=lbp_yt->getRadii()[0]) {
    throw std::runtime_error((boost::format("The T radii R_xt (%f) and R_yt (%f) do not match")% lbp_xt->getRadii()[0] % lbp_yt->getRadii()[0]).str());
  }

}

bob::ip::base::LBPTop::LBPTop(const LBPTop& other)
: m_lbp_xy(other.m_lbp_xy),
  m_lbp_xt(other.m_lbp_xt),
  m_lbp_yt(other.m_lbp_yt)
{
}

bob::ip::base::LBPTop::~LBPTop() { }

bob::ip::base::LBPTop& bob::ip::base::LBPTop::operator= (const LBPTop& other) {
  m_lbp_xy = other.m_lbp_xy;
  m_lbp_xt = other.m_lbp_xt;
  m_lbp_yt = other.m_lbp_yt;
  return *this;
}


/*
 * MWSTest.cpp
 *
 *  Created on: Jun 20, 2012
 *      Author: selman.joe@gmail.com
 */

#define BOOST_TEST_MODULE MWSTest
#define BOOST_TEST_MAIN
#include "../src/config.h"
#ifdef USE_DYNAMIC_UNIT_TEST
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
#include <boost/random/mersenne_twister.hpp>
#include "TestUtilities.h"
#include "../src/inference/MaxWalkSat.h"

BOOST_AUTO_TEST_CASE( MaxWalkSat_simple ) {
    std::string facts(
            "D-P(a) @ [1:10]\n"
            "D-Q(a) @ [1:15]\n");

    std::string formulas(
            "1: [!D-P(a) v P(a)]\n"
            "1: [!P(a) v D-P(a)]\n"
            "1: [!D-Q(a) v Q(a)]\n"
            "1: [!Q(a) v D-Q(a)]\n");

    Domain d = loadDomainWithStreams(facts, formulas);
    MWSSolver solver(&d);
    boost::mt19937 rng(0);
    Model m = solver.run(rng);
    BOOST_CHECK_EQUAL(m.toString(), "D-P(a) @ {[1:10]}\n"
            "D-Q(a) @ {[1:15]}\n"
            "P(a) @ {[1:10]}\n"
            "Q(a) @ {[1:15]}\n");
}

BOOST_AUTO_TEST_CASE( MaxWalkSat_competing ) {
    std::string facts(
            "D-P(a) @ [1:10]\n"
            "D-Q(a) @ [1:15]\n");

    std::string formulas(
            "5: [!D-P(a) v P(a)]\n"
            "5: [!P(a) v D-P(a)]\n"
            "1: [!D-Q(a) v Q(a)]\n"
            "1: [!Q(a) v D-Q(a)]\n"
            "5: [!P(a) v !Q(a)]");

    Domain d = loadDomainWithStreams(facts, formulas);
    MWSSolver solver(&d);
    boost::mt19937 rng(0);
    Model m = solver.run(rng);
    BOOST_CHECK_EQUAL(m.toString(), "D-P(a) @ {[1:10]}\n"
            "D-Q(a) @ {[1:15]}\n"
            "P(a) @ {[1:10]}\n"
            "Q(a) @ {[11:15]}\n");
}


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersSerializeFixture.hpp"
#include "../Deserializer.hpp"

#include <string>
#include <iostream>

BOOST_AUTO_TEST_SUITE(DeserializeParser)

struct ActivationFixture : public ParserFlatbuffersSerializeFixture
{
    explicit ActivationFixture(const std::string& inputShape,
                               const std::string& outputShape,
                               const std::string& dataType,
                               const std::string& activationType="Sigmoid",
                               const std::string& a = "0.0",
                               const std::string& b = "0.0")
    {
        m_JsonString = R"(
        {
            inputIds: [0],
            outputIds: [2],
            layers: [{
                layer_type: "InputLayer",
                layer: {
                    base: {
                        layerBindingId: 0,
                        base: {
                            index: 0,
                            layerName: "InputLayer",
                            layerType: "Input",
                            inputSlots: [{
                                index: 0,
                                connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                            }],
                            outputSlots: [{
                                index: 0,
                                tensorInfo: {
                                    dimensions: )" + inputShape + R"(,
                                    dataType: )" + dataType + R"(
                                },
                            }],
                        },
                    }
                },
            },
            {
                layer_type: "ActivationLayer",
                layer : {
                    base: {
                        index:1,
                        layerName: "ActivationLayer",
                        layerType: "Activation",
                        inputSlots: [{
                            index: 0,
                            connection: {sourceLayerIndex:0, outputSlotIndex:0 },
                        }],
                        outputSlots: [{
                            index: 0,
                            tensorInfo: {
                                dimensions: )" + outputShape + R"(,
                                dataType: )" + dataType + R"(
                            },
                        }],
                    },
                    descriptor: {
                        a: )" + a + R"(,
                        b: )" + b + R"(,
                        activationFunction: )" + activationType + R"(
                    },
                },
            },
            {
                layer_type: "OutputLayer",
                layer: {
                    base:{
                        layerBindingId: 2,
                        base: {
                            index: 2,
                            layerName: "OutputLayer",
                            layerType: "Output",
                            inputSlots: [{
                                index: 0,
                                connection: {sourceLayerIndex:1, outputSlotIndex:0 },
                            }],
                            outputSlots: [{
                                index: 0,
                                tensorInfo: {
                                    dimensions: )" + outputShape + R"(,
                                    dataType: )" + dataType + R"(
                                },
                            }],
                        }
                    }
                },
            }]
        }
        )";
        Setup();
    }
};

struct SimpleActivationFixture : ActivationFixture
{
    SimpleActivationFixture() : ActivationFixture("[1, 2, 2, 1]",
                                                  "[1, 2, 2, 1]",
                                                  "QuantisedAsymm8",
                                                  "ReLu") {}
};

struct SimpleActivationFixture2 : ActivationFixture
{
    SimpleActivationFixture2() : ActivationFixture("[1, 2, 2, 1]",
                                                   "[1, 2, 2, 1]",
                                                   "Float32",
                                                   "ReLu") {}
};

struct SimpleActivationFixture3 : ActivationFixture
{
    SimpleActivationFixture3() : ActivationFixture("[1, 2, 2, 1]",
                                                   "[1, 2, 2, 1]",
                                                   "QuantisedAsymm8",
                                                   "BoundedReLu",
                                                   "5.0",
                                                   "0.0") {}
};

struct SimpleActivationFixture4 : ActivationFixture
{
    SimpleActivationFixture4() : ActivationFixture("[1, 2, 2, 1]",
                                                   "[1, 2, 2, 1]",
                                                   "Float32",
                                                   "BoundedReLu",
                                                   "5.0",
                                                   "0.0") {}
};

BOOST_FIXTURE_TEST_CASE(ActivationReluQuantisedAsymm8, SimpleActivationFixture)
{
    RunTest<4, armnn::DataType::QAsymmU8>(
            0,
            {{"InputLayer", {10, 0, 2, 0}}},
            {{"OutputLayer", {10, 0, 2, 0}}});
}

BOOST_FIXTURE_TEST_CASE(ActivationReluFloat32, SimpleActivationFixture2)
{
    RunTest<4, armnn::DataType::Float32>(
            0,
            {{"InputLayer", {111, -85, 226, 3}}},
            {{"OutputLayer", {111, 0, 226, 3}}});
}

BOOST_FIXTURE_TEST_CASE(ActivationBoundedReluQuantisedAsymm8, SimpleActivationFixture3)
{
    RunTest<4, armnn::DataType::QAsymmU8>(
            0,
            {{"InputLayer", {10, 0, 2, 0}}},
            {{"OutputLayer", {5, 0, 2, 0}}});
}

BOOST_FIXTURE_TEST_CASE(ActivationBoundedReluFloat32, SimpleActivationFixture4)
{
    RunTest<4, armnn::DataType::Float32>(
            0,
            {{"InputLayer", {111, -85, 226, 3}}},
            {{"OutputLayer", {5, 0, 5, 3}}});
}

BOOST_AUTO_TEST_SUITE_END()