/*
(c) 2012 Fengtao Fan
*/
#include "SimpleGraph.h"
#include "SimpleMesh.h"
#include "canonical_loops.h"
#include "edge_annotations_gauss.h"
#include "CycleOptimization/Annotation/AnnotationComputation.h"
#include "psbmReebGraph.h"

#include <boost/progress.hpp>

#include <fstream>
#include <sstream>

#include <map>

//_simpGraph_vec meshGraph;
//_SimpleMesh cycMesh;
//std::vector<Vector3>   meshNormal;
void LoadCycleData(psbmReebGraph &reebGraph, std::vector<std::vector<int> > &basis_h_loops,
                   std::vector<std::vector<int> > &basis_v_loops, std::vector<int> &base_pt_vec) {
    //
    std::vector<bool> initVerticalLoopsType;
    // set the flag bit for handle or tunnel loops
    for (unsigned int i = 0; i < reebGraph.initVerticalLoops->size(); i++) {
        if ((*reebGraph.initVerticalLoops)[i].pathType) {// this is a vertical loop
            initVerticalLoopsType.push_back(true);
        } else {
            initVerticalLoopsType.push_back(false);
        }
    }
    // Store the critical points
    base_pt_vec.reserve(2 * reebGraph.criticalPairing->size());
    for (unsigned int j = 0; j < reebGraph.criticalPairing->size(); j++) {
        base_pt_vec.push_back((*reebGraph.criticalPairing)[j].first);
        base_pt_vec.push_back((*reebGraph.criticalPairing)[j].second);
    }
    //
    const int tot_genus = (int) reebGraph.vecVerticalLoopEdgePathOnMesh_Vertex.size();
    basis_v_loops.reserve(tot_genus);
    basis_h_loops.reserve(tot_genus);
    // store v-basis
    for (int i = 0; i < tot_genus; i++) {
        std::vector<int> tempIntVec;
        int edges_num = 0;
        int row_num = i;
        //
        if (!initVerticalLoopsType[i])
            row_num += tot_genus;
        //
        for (std::list<std::pair<int, int> >::iterator listIter = (reebGraph.invLinkNumbersMatrix)[row_num].begin();
             listIter != (reebGraph.invLinkNumbersMatrix)[row_num].end(); listIter++) {
            int col_num = listIter->first;
            int counter = abs(listIter->second);
            if (col_num >= tot_genus) {// it is a horizontal loop
                edges_num +=
                        counter * (int) (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num - tot_genus].size();
            } else {
                edges_num += counter * (int) (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].size();
            }
        }
        //
        //sstr << edges_num << std::endl;
        tempIntVec.reserve(edges_num + 2);
        for (std::list<std::pair<int, int> >::iterator listIter = (reebGraph.invLinkNumbersMatrix)[row_num].begin();
             listIter != (reebGraph.invLinkNumbersMatrix)[row_num].end(); listIter++) {
            int col_num = listIter->first;
            int counter = abs(listIter->second);
            if (col_num >= tot_genus) {// it is a horizontal loop
                for (int j = 0; j < counter; j++) {
                    for (std::vector<int>::iterator vIter = (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num -
                                                                                tot_genus].begin();
                         vIter !=
                         (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num - tot_genus].end(); vIter++) {
                        //sstr << *vIter << " ";
                        tempIntVec.push_back(*vIter);
                    }
                }
            } else {
                for (int j = 0; j < counter; j++) {
                    for (std::vector<int>::iterator vIter = (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].begin();
                         vIter != (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].end(); vIter++) {
                        //sstr << *vIter << " ";
                        tempIntVec.push_back(*vIter);
                    }
                }
            }
        }
        //
        basis_v_loops.push_back(tempIntVec);
        //sstr << std::endl;
    }
    //store h-basis
    for (int i = 0; i < tot_genus; i++) {
        std::vector<int> hTempIntVec;
        int edges_num = 0;
        int row_num = i;
        //
        if (initVerticalLoopsType[i])
            row_num += tot_genus;
        //
        for (std::list<std::pair<int, int> >::iterator listIter = (reebGraph.invLinkNumbersMatrix)[row_num].begin();
             listIter != (reebGraph.invLinkNumbersMatrix)[row_num].end(); listIter++) {
            int col_num = listIter->first;
            int counter = abs(listIter->second);
            if (col_num >= tot_genus) {// it is a horizontal loop
                edges_num +=
                        counter * (int) (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num - tot_genus].size();
            } else {
                edges_num += counter * (int) (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].size();
            }
        }
        //
        //sstr << edges_num << std::endl;
        hTempIntVec.reserve(edges_num + 2);
        for (std::list<std::pair<int, int> >::iterator listIter = (reebGraph.invLinkNumbersMatrix)[row_num].begin();
             listIter != (reebGraph.invLinkNumbersMatrix)[row_num].end(); listIter++) {
            int col_num = listIter->first;
            int counter = abs(listIter->second);
            if (col_num >= tot_genus) {// it is a horizontal loop
                for (int j = 0; j < counter; j++) {
                    for (std::vector<int>::iterator vIter = (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num -
                                                                                tot_genus].begin();
                         vIter !=
                         (reebGraph.vecHorizontalLoopEdgePathOnMesh_Edge)[col_num - tot_genus].end(); vIter++) {
                        //sstr << *vIter << " ";
                        hTempIntVec.push_back(*vIter);
                    }
                }
            } else {
                for (int j = 0; j < counter; j++) {
                    for (std::vector<int>::iterator vIter = (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].begin();
                         vIter != (reebGraph.vecVerticalLoopEdgePathOnMesh_Edge)[col_num].end(); vIter++) {
                        //sstr << *vIter << " ";
                        hTempIntVec.push_back(*vIter);
                    }
                }
            }
        }
        //sstr << std::endl;
        basis_h_loops.push_back(hTempIntVec);
    }
    return;
}

void AssembleMeshGraph(_simpGraph_vec &meshGraph, _SimpleMesh &mesh, std::vector<float> &sqEdgeLength,
                       std::set<int> &extraVertices, const float fScaleRatio) {
    meshGraph.InitNodes((int) mesh.vecVertex.size());
    //
    std::vector<bool> vertexFlag(mesh.vecVertex.size(), false);
    //
    for (std::set<int>::iterator sIter = extraVertices.begin();
         sIter != extraVertices.end(); sIter++) {
        vertexFlag[*sIter] = true;
    }
    //
    std::vector<int> nonExistEdges;
    //
    float totEdgeLen = 0.0;
    for (unsigned int eid = 0; eid < mesh.vecEdge.size(); eid++) {
        int v_a = mesh.vecEdge[eid].v0;
        int v_b = mesh.vecEdge[eid].v1;

        Vector3 p_a(mesh.vecVertex[v_a].x, mesh.vecVertex[v_a].y, mesh.vecVertex[v_a].z);
        Vector3 p_b(mesh.vecVertex[v_b].x, mesh.vecVertex[v_b].y, mesh.vecVertex[v_b].z);

        //
        sqEdgeLength.push_back(norm(p_a - p_b) * fScaleRatio);
        //

        meshGraph.AddEdge(v_a, v_b, eid);
        //
        totEdgeLen += sqEdgeLength.back();
        //
        if (vertexFlag[v_a] || vertexFlag[v_b]) {
            nonExistEdges.push_back(eid);
        }
    }
    //
    for (unsigned int i = 0; i < nonExistEdges.size(); i++) {
        //
        sqEdgeLength[nonExistEdges[i]] = totEdgeLen;// std::numeric_limits<float>::max(); // totEdgeLen* 2;
    }
    return;
}

void AssembleMeshGraph(_simpGraph_vec &meshGraph, _SimpleMesh &mesh, std::vector<float> &sqEdgeLength,
                       const float fScaleRatio) {
    meshGraph.InitNodes((int) mesh.vecVertex.size());
    //
    for (unsigned int eid = 0; eid < mesh.vecEdge.size(); eid++) {
        int v_a = mesh.vecEdge[eid].v0;
        int v_b = mesh.vecEdge[eid].v1;

        Vector3 p_a(mesh.vecVertex[v_a].x, mesh.vecVertex[v_a].y, mesh.vecVertex[v_a].z);
        Vector3 p_b(mesh.vecVertex[v_b].x, mesh.vecVertex[v_b].y, mesh.vecVertex[v_b].z);

        //
        sqEdgeLength.push_back(norm(p_a - p_b) * fScaleRatio);
        //

        meshGraph.AddEdge(v_a, v_b, eid);
    }
}

float BasisWeight(std::vector<std::set<int> > &basis, std::vector<float> sqEdgeLen) {
    float tot_len = 0.0;
    for (unsigned int i = 0; i < basis.size(); i++) {
        for (std::set<int>::iterator sIter = basis[i].begin();
             sIter != basis[i].end(); sIter++) {
            tot_len += sqEdgeLen[*sIter];
        }
    }
    return tot_len;
}

float BasisWeight(std::vector<std::vector<int> > &basis, std::vector<float> sqEdgeLen) {
    float tot_len = 0.0;
    for (unsigned int i = 0; i < basis.size(); i++) {
        for (std::vector<int>::iterator vIter = basis[i].begin();
             vIter != basis[i].end(); vIter++) {
            tot_len += sqEdgeLen[*vIter];
        }
    }
    return tot_len;
}

void CycleLocalOptimization_bdry(_SimpleMesh &locMesh, psbmReebGraph &reebgraph, std::vector<int> &OrientTriangles,
                                 std::vector<std::set<int> > &out_v_basis_loops,
                                 std::vector<std::set<int> > &out_h_basis_loops,
                                 std::set<int> extraVertices,
                                 const float fScaleRatio) {//
    _simpGraph_vec locMeshGraph;
    //
    edge_annotation_computing edge_anno_proxy;
    //
    //
    ComputeAnnotation(locMesh, edge_anno_proxy.edge_annotations, OrientTriangles);
    edge_anno_proxy.vec_size = (int) edge_anno_proxy.edge_annotations[0].size();
    //
    if (edge_anno_proxy.vec_size == 0) {
        std::cout << "TRIVIAL LOOPS" << std::endl;
        exit(0);
    }
    std::vector<float> sqEdgeLen;
    AssembleMeshGraph(locMeshGraph, locMesh, sqEdgeLen, extraVertices, fScaleRatio);
    //
    edge_anno_proxy.SetMeshPtr(&locMesh);
    //edge_anno_proxy.ReadEdgeAnnotationsFromFile(argv[2]);
//{//
//	//
//	edge_annotation_computing edge_anno_proxy;
//	//
//	//
//	LoadData(argv[1], edge_anno_proxy);
//	//
//	std::set<int> extraVertices;
//	ReadBoundaries(argv[argc - 1], extraVertices);
//	//
//	std::vector<float> sqEdgeLen;
//	AssembleMeshGraph(meshGraph, cycMesh, sqEdgeLen, extraVertices);
//	//
//	edge_anno_proxy.SetMeshPtr(&cycMesh);
//	//edge_anno_proxy.ReadEdgeAnnotationsFromFile(argv[2]);
    //
    std::vector<std::vector<int> > basis_h_loops;
    std::vector<std::vector<int> > basis_v_loops;
    //
    std::vector<Annotation_Type> basis_h_annotations;
    std::vector<Annotation_Type> basis_v_annotations;
    //
    std::vector<int> base_pt_vec;
    //
    LoadCycleData(reebgraph, basis_h_loops, basis_v_loops, base_pt_vec);
    //ReadBasisLoops(argv[4], basis_h_loops);
    //ReadBasisLoops(argv[5], basis_v_loops);

    edge_anno_proxy.CheckTwoGroupVectorOrthogonality(basis_v_loops, basis_v_annotations, basis_h_loops,
                                                     basis_h_annotations);
    //
    canonical_loops_computing cano_loop_proxy;
    //
    //
    //std::set<int> base_pt_set;
    //for (unsigned int i = 0; i < basis_v_loops.size(); i++)
    //{
    //	for (unsigned int j = 0; j < basis_v_loops[i].size(); j++)
    //	{
    //		base_pt_set.insert(mesh.vecEdge[basis_v_loops[i][j]].v0);
    //		base_pt_set.insert(mesh.vecEdge[basis_v_loops[i][j]].v1);
    //	}
    //}
    //std::vector<int> base_pt_vec(base_pt_set.begin(), base_pt_set.end());//
    //
    //std::vector<int> base_pt_vec;
    //ReadBasisCriticalPoints(argv[3], base_pt_vec);
    std::set<int> base_pt_set(base_pt_vec.begin(), base_pt_vec.end());
    //
    //std::cout << "old base_pt_vec " << base_pt_vec.size() << std::endl;
    //
    cano_loop_proxy.SetGraphPtr(&locMeshGraph);
    cano_loop_proxy.SetEdgeWeights(sqEdgeLen);
    cano_loop_proxy.SetBasePointArray(base_pt_vec);
    cano_loop_proxy.SetMeshPtr(&locMesh);
    cano_loop_proxy.SetMeshGenus(edge_anno_proxy.vec_size / 2);
    //
    //cano_loop_proxy.ResetBaseVertices(basis_h_loops, edge_anno_proxy);
    //std::cout << "new base_pt_vec " << cano_loop_proxy.base_pt_index.size() << std::endl;
    //
    //bool bPartialBasis = false;
    std::cout << "Time for shortening handle and tunnel loops : " << std::endl;
    float cur_basis_h_len = BasisWeight(basis_h_loops, sqEdgeLen);
    float cur_basis_v_len = BasisWeight(basis_v_loops, sqEdgeLen);
    std::vector<std::set<int> > tmp_short_h_basis_loop;
    std::vector<std::set<int> > tmp_short_v_basis_loop;
    {
        // starting measuring the time
        boost::progress_timer t;
        //
        std::vector<std::set<int> > cano_loops;
        std::vector<int> non_tree_edges;
        std::vector<Annotation_Type> cano_loop_annotations;
        std::set<std::pair<float, int>, myFloatIntPairLessThan> sorted_cano_loops;
        std::vector<int> non_tree_edges_for_sorted_cano_loops;
        //
        float pre_basis_h_len = 0.0;
        float pre_basis_v_len = 0.0;

        //
        int iterNumber = 0;
        // optimize the all loops together
        do {
            tmp_short_h_basis_loop.clear();
            tmp_short_h_basis_loop.reserve(basis_h_loops.size());
            tmp_short_v_basis_loop.clear();
            tmp_short_v_basis_loop.reserve(basis_v_loops.size());
            base_pt_vec.clear();
            //
            cano_loop_proxy.compute_canonical_loops_fibo(cano_loops, non_tree_edges, cano_loop_annotations,
                                                         sorted_cano_loops, edge_anno_proxy);
            //
            if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_h_loops, basis_h_annotations, cano_loops,
                                                              non_tree_edges,
                                                              cano_loop_annotations, sorted_cano_loops,
                                                              tmp_short_h_basis_loop,
                                                              non_tree_edges_for_sorted_cano_loops)) {
                std::cout << ".";//ompletly use short loops" << std::endl;
            } else {
                std::cout << "*"; //artially use short loops" << std::endl;
                //bPartialBasis = true;
            }
            /**/
            cano_loop_proxy.base_pt_index.clear();//
            cano_loop_proxy.base_pt_index.reserve(non_tree_edges_for_sorted_cano_loops.size() * 3);
            for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
                int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
                //std::cout << edge_idx << std::endl;
                if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                    base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                    cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
                }
                //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
                //{
                //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
                //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
                //}
            }

            if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_v_loops, basis_v_annotations, cano_loops,
                                                              non_tree_edges,
                                                              cano_loop_annotations, sorted_cano_loops,
                                                              tmp_short_v_basis_loop,
                                                              non_tree_edges_for_sorted_cano_loops)) {
                std::cout << "."; //ompletly use short loops" << std::endl;
            } else {
                std::cout << "*"; //artially use short loops" << std::endl;
                //bPartialBasis = true;
            }
            //
            pre_basis_v_len = cur_basis_v_len;
            pre_basis_h_len = cur_basis_h_len;
            cur_basis_v_len = BasisWeight(tmp_short_v_basis_loop, sqEdgeLen);
            cur_basis_h_len = BasisWeight(tmp_short_h_basis_loop, sqEdgeLen);
            //
            for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
                int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
                //std::cout << edge_idx << std::endl;
                if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                    base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                    cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
                }
                //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
                //{
                //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
                //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
                //}
            }
            //std::cout << iterNumber << "\t";//<< " new base pt set size " << base_pt_set.size() << std::endl;
            //std::cout << iterNumber << " new base pt size " << cano_loop_proxy.base_pt_index.size() << std::endl;
            iterNumber++;
            if (cano_loop_proxy.base_pt_index.empty())
                break;
            if (iterNumber > 2000)
                break;

        } while (abs(cur_basis_h_len - pre_basis_h_len) > 0.000001 ||
                 abs(cur_basis_v_len - pre_basis_v_len) > 0.000001);
        //
        std::cout << std::endl;
    }
    //
    //
    out_h_basis_loops = tmp_short_h_basis_loop;
    //
    out_v_basis_loops = tmp_short_v_basis_loop;
    return;
}

void CycleGlobalOptimization(_SimpleMesh &locMesh, psbmReebGraph &reebgraph, std::vector<int> &OrientTriangles,
                             std::vector<std::set<int> > &out_v_basis_loops,
                             std::vector<std::set<int> > &out_h_basis_loops,
                             const float fScaleRatio) {//
    _simpGraph_vec locMeshGraph;
    //
    edge_annotation_computing edge_anno_proxy;
    //
    //
    ComputeAnnotation(locMesh, edge_anno_proxy.edge_annotations, OrientTriangles);
    edge_anno_proxy.vec_size = (int) edge_anno_proxy.edge_annotations[0].size();
    //
    if (edge_anno_proxy.vec_size == 0) {
        std::cout << "TRIVIAL LOOPS" << std::endl;
        exit(0);
    }
    std::vector<float> sqEdgeLen;
    AssembleMeshGraph(locMeshGraph, locMesh, sqEdgeLen, fScaleRatio);
    //
    edge_anno_proxy.SetMeshPtr(&locMesh);
    //edge_anno_proxy.ReadEdgeAnnotationsFromFile(argv[2]);
//{
//	//
//	edge_annotation_computing edge_anno_proxy;
//	//
//	//std::vector<int> meshTriangles;
//	LoadData(argv[1], edge_anno_proxy);
//	//
//	std::vector<float> sqEdgeLen;
//	AssembleMeshGraph(meshGraph, cycMesh, sqEdgeLen);
//	//
//	edge_anno_proxy.SetMeshPtr(&cycMesh);
//	//edge_anno_proxy.ReadEdgeAnnotationsFromFile(argv[2]);
//	//
//	//std::vector<int> fvs;
//	//ComputeFeedbackVertexSet(edge_anno_proxy.edge_annotations, fvs);

    std::vector<std::vector<int> > basis_h_loops;
    std::vector<std::vector<int> > basis_v_loops;
    std::vector<Annotation_Type> basis_h_annotations;
    std::vector<Annotation_Type> basis_v_annotations;
    //
    std::vector<int> base_pt_vec;
    //
    LoadCycleData(reebgraph, basis_h_loops, basis_v_loops, base_pt_vec);
    //
    edge_anno_proxy.CheckTwoGroupVectorOrthogonality(basis_v_loops, basis_v_annotations, basis_h_loops,
                                                     basis_h_annotations);
    //
    canonical_loops_computing cano_loop_proxy;
    //

    //ReadBasisCriticalPoints(argv[3], base_pt_vec);
    std::set<int> base_pt_set(base_pt_vec.begin(), base_pt_vec.end());
    //
    std::cout << "old base_pt_vec " << base_pt_vec.size() << std::endl;
    //
    cano_loop_proxy.SetGraphPtr(&locMeshGraph);
    cano_loop_proxy.SetEdgeWeights(sqEdgeLen);
    cano_loop_proxy.SetBasePointArray(base_pt_vec);
    cano_loop_proxy.SetMeshPtr(&locMesh);
    cano_loop_proxy.SetMeshGenus(edge_anno_proxy.vec_size / 2);
    //
    //cano_loop_proxy.ResetBaseVertices(basis_h_loops, edge_anno_proxy);
    //std::cout << "new base_pt_vec " << cano_loop_proxy.base_pt_index.size() << std::endl;
    //
    std::cout << "in cano loops computation" << std::endl;
    // starting measuring time
    boost::progress_timer t;
    //
    std::vector<std::set<int> > cano_loops;
    std::vector<int> non_tree_edges;
    std::vector<Annotation_Type> cano_loop_annotations;
    std::set<std::pair<float, int>, myFloatIntPairLessThan> sorted_cano_loops;
    std::vector<int> non_tree_edges_for_sorted_cano_loops;
    //
    float pre_basis_h_len = 0.0;
    float pre_basis_v_len = 0.0;
    float cur_basis_h_len = BasisWeight(basis_h_loops, sqEdgeLen);
    float cur_basis_v_len = BasisWeight(basis_v_loops, sqEdgeLen);
    std::vector<std::set<int> > tmp_short_h_basis_loop;
    std::vector<std::set<int> > tmp_short_v_basis_loop;
    //
    int iterNumber = 0;
    // compute the vertical loops first
    do {
        tmp_short_h_basis_loop.clear();
        tmp_short_h_basis_loop.reserve(basis_h_loops.size());
        tmp_short_v_basis_loop.clear();
        tmp_short_v_basis_loop.reserve(basis_v_loops.size());
        base_pt_vec.clear();
        //
        cano_loop_proxy.compute_canonical_loops_fibo(cano_loops, non_tree_edges, cano_loop_annotations,
                                                     sorted_cano_loops, edge_anno_proxy);
        //
        if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_h_loops, basis_h_annotations, cano_loops,
                                                          non_tree_edges,
                                                          cano_loop_annotations, sorted_cano_loops,
                                                          tmp_short_h_basis_loop,
                                                          non_tree_edges_for_sorted_cano_loops)) {
            std::cout << "c \t ";//ompletly use short loops" << std::endl;
        } else {
            std::cout << "p \t"; //artially use short loops" << std::endl;
        }
        /**/
        cano_loop_proxy.base_pt_index.clear();//
        cano_loop_proxy.base_pt_index.reserve(non_tree_edges_for_sorted_cano_loops.size() * 3);
        for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
            int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
            //std::cout << edge_idx << std::endl;
            if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
            }
            //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
            //{
            //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
            //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
            //}
        }

        if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_v_loops, basis_v_annotations, cano_loops,
                                                          non_tree_edges,
                                                          cano_loop_annotations, sorted_cano_loops,
                                                          tmp_short_v_basis_loop,
                                                          non_tree_edges_for_sorted_cano_loops)) {
            std::cout << "c \t"; //ompletly use short loops" << std::endl;
        } else {
            std::cout << "p \t"; //artially use short loops" << std::endl;
        }
        //
        pre_basis_v_len = cur_basis_v_len;
        pre_basis_h_len = cur_basis_h_len;
        cur_basis_v_len = BasisWeight(tmp_short_v_basis_loop, sqEdgeLen);
        cur_basis_h_len = BasisWeight(tmp_short_h_basis_loop, sqEdgeLen);
        //
        for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
            int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
            //std::cout << edge_idx << std::endl;
            if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
            }
            //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
            //{
            //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
            //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
            //}
        }
        std::cout << iterNumber << "\t";//<< " new base pt set size " << base_pt_set.size() << std::endl;
        //std::cout << iterNumber << " new base pt size " << cano_loop_proxy.base_pt_index.size() << std::endl;
        iterNumber++;
        if (cano_loop_proxy.base_pt_index.empty())
            break;
        if (iterNumber > 2000)
            break;

    } while (abs(cur_basis_h_len - pre_basis_h_len) > 0.000001 || abs(cur_basis_v_len - pre_basis_v_len) > 0.000001);
    //
    // use optimzed vertical loops to optimize horizontal loops
    std::set<int> tmpSet;
    cano_loop_proxy.base_pt_index.clear();
    for (unsigned int i = 0; i < tmp_short_v_basis_loop.size(); i++) {
        for (std::set<int>::iterator sIter = tmp_short_v_basis_loop[i].begin();
             sIter != tmp_short_v_basis_loop[i].end(); sIter++) {
            base_pt_set.insert(locMesh.vecEdge[*sIter].v0);
            base_pt_set.insert(locMesh.vecEdge[*sIter].v1);
            tmpSet.insert(locMesh.vecEdge[*sIter].v0);
        }
    }
    //
    for (unsigned int i = 0; i < tmp_short_h_basis_loop.size(); i++) {
        for (std::set<int>::iterator sIter = tmp_short_h_basis_loop[i].begin();
             sIter != tmp_short_h_basis_loop[i].end(); sIter++) {
            base_pt_set.insert(locMesh.vecEdge[*sIter].v0);
            base_pt_set.insert(locMesh.vecEdge[*sIter].v1);
            tmpSet.insert(locMesh.vecEdge[*sIter].v0);
        }
    }
    //
    cano_loop_proxy.base_pt_index.assign(tmpSet.begin(), tmpSet.end());
    //
    tmp_short_h_basis_loop.clear();
    tmp_short_h_basis_loop.reserve(basis_h_loops.size());
    tmp_short_v_basis_loop.clear();
    tmp_short_v_basis_loop.reserve(basis_v_loops.size());
    //
    cano_loop_proxy.compute_canonical_loops_fibo(cano_loops, non_tree_edges, cano_loop_annotations, sorted_cano_loops,
                                                 edge_anno_proxy);
    //

    if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_v_loops, basis_v_annotations, cano_loops, non_tree_edges,
                                                      cano_loop_annotations, sorted_cano_loops, tmp_short_v_basis_loop,
                                                      non_tree_edges_for_sorted_cano_loops)) {
        std::cout << "c \t"; //ompletly use short loops" << std::endl;
    } else {
        std::cout << "p \t"; //artially use short loops" << std::endl;
    }
    //
    if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_h_loops, basis_h_annotations, cano_loops, non_tree_edges,
                                                      cano_loop_annotations, sorted_cano_loops, tmp_short_h_basis_loop,
                                                      non_tree_edges_for_sorted_cano_loops)) {
        std::cout << "completly use short loops" << std::endl;
    } else {
        std::cout << "partially use short loops" << std::endl;
    }
    //
    out_h_basis_loops = tmp_short_h_basis_loop;
    //
    out_v_basis_loops = tmp_short_v_basis_loop;
    return;
}
//
//int mainx(int argc, char** argv)
//{
//	int optMethodOption;
//	stringstream sstr(stringstream::in | stringstream::out);
//	if (argc == 8)
//	{
//		sstr.str(argv[7]);
//		sstr >> optMethodOption;
//		sstr.clear();
//		std::cout << optMethodOption << std::endl;
//		if (optMethodOption)
//		{
//			//GlobalOptimization(argc, argv);
//		}
//		else
//		{
//			LocalOptimization(argc, argv);
//		}
//	}
//	else
//	{
//		if (argc == 9)
//			LocalOptimization_bdry(argc, argv);
//		else
//			LocalOptimization(argc, argv);
//	}
//	return 1;
//	//
//}

void CycleLocalOptimization(_SimpleMesh &locMesh, psbmReebGraph &reebgraph, std::vector<int> &OrientTriangles,
                            std::vector<std::set<int> > &out_v_basis_loops,
                            std::vector<std::set<int> > &out_h_basis_loops,
                            const float fScaleRatio) {//
    _simpGraph_vec locMeshGraph;
    //
    edge_annotation_computing edge_anno_proxy;
    //
    //std::cout << "tris size" << OrientTriangles.size() << std::endl;
    //
    ComputeAnnotation(locMesh, edge_anno_proxy.edge_annotations, OrientTriangles);
    edge_anno_proxy.vec_size = (int) edge_anno_proxy.edge_annotations[0].size();
    //
    if (edge_anno_proxy.vec_size == 0) {
        std::cout << "TRIVIAL LOOPS" << std::endl;
        exit(0);
    }
    //std::cout << "before assembe graph " << edge_anno_proxy.vec_size << std::endl;
    std::vector<float> sqEdgeLen;
    AssembleMeshGraph(locMeshGraph, locMesh, sqEdgeLen, fScaleRatio);
    //
    edge_anno_proxy.SetMeshPtr(&locMesh);
    //edge_anno_proxy.ReadEdgeAnnotationsFromFile(argv[2]);
    //
    std::vector<int> basis_h_LowestOnePos;
    std::vector<int> basis_v_LowestOnePos;
    std::vector<Annotation_Type> reduced_basis_h_annotations;
    std::vector<Annotation_Type> reduced_basis_v_annotations;
    //
    std::vector<std::vector<int> > basis_h_loops;
    std::vector<std::vector<int> > basis_v_loops;
    std::vector<Annotation_Type> basis_h_annotations;
    std::vector<Annotation_Type> basis_v_annotations;
    //
    std::vector<int> base_pt_vec;
    //
    LoadCycleData(reebgraph, basis_h_loops, basis_v_loops, base_pt_vec);

    edge_anno_proxy.CheckTwoGroupVectorOrthogonality(basis_v_loops, basis_v_annotations, reduced_basis_v_annotations,
                                                     basis_v_LowestOnePos,
                                                     basis_h_loops, basis_h_annotations, reduced_basis_h_annotations,
                                                     basis_h_LowestOnePos);
    //
    canonical_loops_computing cano_loop_proxy;
    //
    //
    //std::set<int> base_pt_set;
    //for (unsigned int i = 0; i < basis_v_loops.size(); i++)
    //{
    //	for (unsigned int j = 0; j < basis_v_loops[i].size(); j++)
    //	{
    //		base_pt_set.insert(mesh.vecEdge[basis_v_loops[i][j]].v0);
    //		base_pt_set.insert(mesh.vecEdge[basis_v_loops[i][j]].v1);
    //	}
    //}
    //std::vector<int> base_pt_vec(base_pt_set.begin(), base_pt_set.end());//
    //

    //ReadBasisCriticalPoints(argv[3], base_pt_vec);
    std::set<int> base_pt_set(base_pt_vec.begin(), base_pt_vec.end());
    //
    //std::cout << "old base_pt_vec " << base_pt_vec.size() << std::endl;
    //
    cano_loop_proxy.SetGraphPtr(&locMeshGraph);
    cano_loop_proxy.SetEdgeWeights(sqEdgeLen);
    cano_loop_proxy.SetBasePointArray(base_pt_vec);
    cano_loop_proxy.SetMeshPtr(&locMesh);
    cano_loop_proxy.SetMeshGenus(edge_anno_proxy.vec_size / 2);
    //
    //cano_loop_proxy.ResetBaseVertices(basis_h_loops, edge_anno_proxy);
    //std::cout << "new base_pt_vec " << cano_loop_proxy.base_pt_index.size() << std::endl;
    //
    std::cout << "Time for shortening handle and tunnel loops : " << std::endl;
    float cur_basis_h_len = BasisWeight(basis_h_loops, sqEdgeLen);
    float cur_basis_v_len = BasisWeight(basis_v_loops, sqEdgeLen);
    std::vector<std::set<int> > tmp_short_h_basis_loop;
    std::vector<std::set<int> > tmp_short_v_basis_loop;
    {
        // starting measuring the time
        boost::progress_timer t;
        //
        std::vector<std::set<int> > cano_loops;
        std::vector<int> non_tree_edges;
        std::vector<Annotation_Type> cano_loop_annotations;
        std::set<std::pair<float, int>, myFloatIntPairLessThan> sorted_cano_loops;
        std::vector<int> non_tree_edges_for_sorted_cano_loops;
        //
        float pre_basis_h_len = 0.0;
        float pre_basis_v_len = 0.0;

        //
        int iterNumber = 0;
        // optimize the all loops together
        do {
            tmp_short_h_basis_loop.clear();
            tmp_short_h_basis_loop.reserve(basis_h_loops.size());
            tmp_short_v_basis_loop.clear();
            tmp_short_v_basis_loop.reserve(basis_v_loops.size());
            base_pt_vec.clear();
            //

            cano_loop_proxy.compute_canonical_loops_fibo(cano_loops, non_tree_edges, cano_loop_annotations,
                                                         sorted_cano_loops, edge_anno_proxy);

            //
            //std::cout << "in cano 1" << std::endl;
            if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_h_loops, basis_h_annotations,
                                                              reduced_basis_h_annotations, basis_h_LowestOnePos,
                                                              cano_loops, non_tree_edges,
                                                              cano_loop_annotations, sorted_cano_loops,
                                                              tmp_short_h_basis_loop,
                                                              non_tree_edges_for_sorted_cano_loops)) {
                std::cout << ".";//ompletly use short loops" << std::endl;
            } else {
                std::cout << "*"; //artially use short loops" << std::endl;
            }
            //std::cout << "out cano 1" << std::endl;
            /**/
            cano_loop_proxy.base_pt_index.clear();//
            cano_loop_proxy.base_pt_index.reserve(non_tree_edges_for_sorted_cano_loops.size() * 3);
            for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
                int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
                //std::cout << edge_idx << std::endl;
                if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                    base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                    cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
                }
                //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
                //{
                //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
                //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
                //}
            }

            //std::cout << "in cano 2" << std::endl;
            if (edge_anno_proxy.ComputeShortestCanonicalLoops(basis_v_loops, basis_v_annotations,
                                                              reduced_basis_v_annotations, basis_v_LowestOnePos,
                                                              cano_loops, non_tree_edges,
                                                              cano_loop_annotations, sorted_cano_loops,
                                                              tmp_short_v_basis_loop,
                                                              non_tree_edges_for_sorted_cano_loops)) {
                std::cout << "."; //ompletly use short loops" << std::endl;
            } else {
                std::cout << "*"; //artially use short loops" << std::endl;
            }
            //std::cout << "out cano 2" << std::endl;
            //
            pre_basis_v_len = cur_basis_v_len;
            pre_basis_h_len = cur_basis_h_len;
            cur_basis_v_len = BasisWeight(tmp_short_v_basis_loop, sqEdgeLen);
            cur_basis_h_len = BasisWeight(tmp_short_h_basis_loop, sqEdgeLen);
            //
            for (unsigned int i = 0; i < non_tree_edges_for_sorted_cano_loops.size(); i++) {
                int edge_idx = non_tree_edges_for_sorted_cano_loops[i];
                //std::cout << edge_idx << std::endl;
                if ((edge_idx >= 0) && base_pt_set.find(locMesh.vecEdge[edge_idx].v0) == base_pt_set.end()) {
                    base_pt_set.insert(locMesh.vecEdge[edge_idx].v0);
                    cano_loop_proxy.base_pt_index.push_back(locMesh.vecEdge[edge_idx].v0);
                }
                //if (base_pt_set.find(mesh.vecEdge[edge_idx].v1) == base_pt_set.end())
                //{
                //	base_pt_set.insert(mesh.vecEdge[edge_idx].v1);
                //	cano_loop_proxy.base_pt_index.push_back(mesh.vecEdge[edge_idx].v1);
                //}
            }
            //std::cout << iterNumber << "\t";//<< " new base pt set size " << base_pt_set.size() << std::endl;
            //std::cout << iterNumber << " new base pt size " << cano_loop_proxy.base_pt_index.size() << std::endl;
            iterNumber++;
            if (cano_loop_proxy.base_pt_index.empty())
                break;
            if (iterNumber > 2000)
                break;

        } while (fabs(cur_basis_h_len - pre_basis_h_len) > 0.000001 ||
                 fabs(cur_basis_v_len - pre_basis_v_len) > 0.000001);
        //
        std::cout << std::endl;
    }
    //
    out_h_basis_loops = tmp_short_h_basis_loop;
    //
    //
    out_v_basis_loops = tmp_short_v_basis_loop;
    //
    return;
}


// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Tov.hpp"

// Need Boost MultiArray because it is used internally by ODEINT
#include "DataStructures/BoostMultiArray.hpp"  // IWYU pragma: keep

#include <algorithm>
#include <array>
#include <boost/numeric/odeint.hpp>  // IWYU pragma: keep
#include <cmath>
#include <cstddef>
#include <functional>
#include <ostream>
#include <pup.h>
#include <vector>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
#include "Utilities/ConstantExpressions.hpp"
#include "Utilities/ContainerHelpers.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/MakeWithValue.hpp"

// IWYU pragma: no_include <boost/numeric/odeint/integrate/integrate_adaptive.hpp>
// IWYU pragma: no_include <boost/numeric/odeint/stepper/controlled_runge_kutta.hpp>
// IWYU pragma: no_include <boost/numeric/odeint/stepper/dense_output_runge_kutta.hpp>
// IWYU pragma: no_include <boost/numeric/odeint/stepper/generation/make_dense_output.hpp>
// IWYU pragma: no_include <boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp>
// IWYU pragma: no_include <complex>

// IWYU pragma: no_forward_declare boost::numeric::odeint::controlled_runge_kutta
// IWYU pragma: no_forward_declare EquationsOfState::EquationOfState
// IWYU pragma: no_forward_declare Tensor

namespace {

void lindblom_rhs(const gsl::not_null<std::array<double, 2>*> dvars,
                  const std::array<double, 2>& vars, const double log_enthalpy,
                  const EquationsOfState::EquationOfState<true, 1>&
                      equation_of_state) noexcept {
  const double& radius_squared = vars[0];
  const double& mass_over_radius = vars[1];
  double& d_radius_squared = (*dvars)[0];
  double& d_mass_over_radius = (*dvars)[1];
  const double specific_enthalpy = std::exp(log_enthalpy);
  const double rest_mass_density =
      get(equation_of_state.rest_mass_density_from_enthalpy(
          Scalar<double>{specific_enthalpy}));
  const double pressure = get(equation_of_state.pressure_from_density(
      Scalar<double>{rest_mass_density}));
  const double energy_density =
      specific_enthalpy * rest_mass_density - pressure;

  // At the center of the star: (u,v) = (0,0)
  if (UNLIKELY((radius_squared == 0.0) and (mass_over_radius == 0.0))) {
    d_radius_squared = -3.0 / (2.0 * M_PI * (energy_density + 3.0 * pressure));
    d_mass_over_radius =
        -2.0 * energy_density / (energy_density + 3.0 * pressure);
  } else {
    const double common_factor =
        (1.0 - 2.0 * mass_over_radius) /
        (4.0 * M_PI * radius_squared * pressure + mass_over_radius);
    d_radius_squared = -2.0 * radius_squared * common_factor;
    d_mass_over_radius =
        -(4.0 * M_PI * radius_squared * energy_density - mass_over_radius) *
        common_factor;
  }
}

class Observer {
 public:
  void operator()(const std::array<double, 2>& vars,
                  const double current_log_enthalpy) noexcept {
    radius.push_back(std::sqrt(vars[0]));
    mass_over_radius.push_back(vars[1]);
    log_enthalpy.push_back(current_log_enthalpy);
  }
  std::vector<double> radius;
  std::vector<double> mass_over_radius;
  std::vector<double> log_enthalpy;
};

template <typename DataType>
RelativisticEuler::Solutions::TovStar<
    gr::Solutions::TovSolution>::RadialVariables<DataType>
interior_solution(
    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
    const DataType& radius, const DataType& mass_over_radius,
    const DataType& log_specific_enthalpy,
    const double log_lapse_at_outer_radius) noexcept {
  RelativisticEuler::Solutions::TovStar<
      gr::Solutions::TovSolution>::RadialVariables<DataType>
      result(radius);
  result.specific_enthalpy = Scalar<DataType>{exp(log_specific_enthalpy)};
  result.rest_mass_density = equation_of_state.rest_mass_density_from_enthalpy(
      result.specific_enthalpy);
  result.pressure =
      equation_of_state.pressure_from_density(result.rest_mass_density);
  result.specific_internal_energy =
      equation_of_state.specific_internal_energy_from_density(
          result.rest_mass_density);
  result.metric_time_potential =
      log_lapse_at_outer_radius - log_specific_enthalpy;
  result.dr_metric_time_potential =
      (mass_over_radius / radius + 4.0 * M_PI * get(result.pressure) * radius) /
      (1.0 - 2.0 * mass_over_radius);
  result.metric_radial_potential = -0.5 * log(1.0 - 2.0 * mass_over_radius);
  result.dr_metric_radial_potential =
      (4.0 * M_PI * radius *
           (get(result.specific_enthalpy) * get(result.rest_mass_density) -
            get(result.pressure)) -
       mass_over_radius / radius) /
      (1.0 - 2.0 * mass_over_radius);
  result.metric_angular_potential = make_with_value<DataType>(radius, 0.0);
  result.dr_metric_angular_potential = make_with_value<DataType>(radius, 0.0);
  return result;
}

template <typename DataType>
RelativisticEuler::Solutions::TovStar<
    gr::Solutions::TovSolution>::RadialVariables<DataType>
vacuum_solution(const DataType& radius, const double total_mass) noexcept {
  RelativisticEuler::Solutions::TovStar<
      gr::Solutions::TovSolution>::RadialVariables<DataType>
      result(radius);
  result.specific_enthalpy = make_with_value<Scalar<DataType>>(radius, 1.0);
  result.rest_mass_density = make_with_value<Scalar<DataType>>(radius, 0.0);
  result.pressure = make_with_value<Scalar<DataType>>(radius, 0.0);
  result.specific_internal_energy =
      make_with_value<Scalar<DataType>>(radius, 0.0);
  const DataType one_minus_two_m_over_r = 1.0 - 2.0 * total_mass / radius;
  result.metric_time_potential = 0.5 * log(one_minus_two_m_over_r);
  result.dr_metric_time_potential =
      total_mass / square(radius) / one_minus_two_m_over_r;
  result.metric_radial_potential = -result.metric_time_potential;
  result.dr_metric_radial_potential = -result.dr_metric_time_potential;
  result.metric_angular_potential = make_with_value<DataType>(radius, 0.0);
  result.dr_metric_angular_potential = make_with_value<DataType>(radius, 0.0);
  return result;
}

}  // namespace

namespace gr::Solutions {

TovSolution::TovSolution(
    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
    const double central_mass_density,
    const double log_enthalpy_at_outer_radius, const double absolute_tolerance,
    const double relative_tolerance) {
  std::array<double, 2> u_and_v = {{0.0, 0.0}};
  std::array<double, 2> dudh_and_dvdh{};
  const double central_log_enthalpy =
      std::log(get(equation_of_state.specific_enthalpy_from_density(
          Scalar<double>{central_mass_density})));
  lindblom_rhs(&dudh_and_dvdh, u_and_v, central_log_enthalpy,
               equation_of_state);
  const double initial_step = -std::min(std::abs(1.0 / dudh_and_dvdh[0]),
                                        std::abs(1.0 / dudh_and_dvdh[1]));
  using StateDopri5 =
      boost::numeric::odeint::runge_kutta_dopri5<std::array<double, 2>>;
  boost::numeric::odeint::dense_output_runge_kutta<
      boost::numeric::odeint::controlled_runge_kutta<StateDopri5>>
      dopri5 = make_dense_output(absolute_tolerance, relative_tolerance,
                                 StateDopri5{});
  Observer observer{};
  boost::numeric::odeint::integrate_adaptive(
      dopri5,
      [&equation_of_state](const std::array<double, 2>& lindblom_u_and_v,
                           std::array<double, 2>& lindblom_dudh_and_dvdh,
                           const double lindblom_enthalpy) noexcept {
        return lindblom_rhs(&lindblom_dudh_and_dvdh, lindblom_u_and_v,
                            lindblom_enthalpy, equation_of_state);
      },
      u_and_v, central_log_enthalpy, log_enthalpy_at_outer_radius, initial_step,
      std::ref(observer));
  outer_radius_ = observer.radius.back();
  const double total_mass_over_radius = observer.mass_over_radius.back();
  total_mass_ = total_mass_over_radius * outer_radius_;
  log_lapse_at_outer_radius_ = 0.5 * log(1.0 - 2.0 * total_mass_over_radius);
  mass_over_radius_interpolant_ =
      intrp::BarycentricRational(observer.radius, observer.mass_over_radius, 5);
  // log_enthalpy(radius) is almost linear so an interpolant of order 3
  // maximizes precision
  log_enthalpy_interpolant_ =
      intrp::BarycentricRational(observer.radius, observer.log_enthalpy, 3);
}

double TovSolution::outer_radius() const noexcept { return outer_radius_; }

double TovSolution::mass_over_radius(const double r) const noexcept {
  ASSERT(r >= 0.0 and r <= outer_radius_,
         "Invalid radius: " << r << " not in [0.0, " << outer_radius_ << "]\n");
  return mass_over_radius_interpolant_(r);
}

double TovSolution::mass(const double r) const noexcept {
  return mass_over_radius(r) * r;
}

double TovSolution::log_specific_enthalpy(const double r) const noexcept {
  ASSERT(r >= 0.0 and r <= outer_radius_,
         "Invalid radius: " << r << " not in [0.0, " << outer_radius_ << "]\n");
  return log_enthalpy_interpolant_(r);
}

template <>
RelativisticEuler::Solutions::TovStar<TovSolution>::RadialVariables<double>
TovSolution::radial_variables(
    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
    const tnsr::I<double, 3>& x) const noexcept {
  // add small number to avoid FPEs at origin
  const double radius = get(magnitude(x)) + 1.e-30 * outer_radius_;
  if (radius >= outer_radius_) {
    return vacuum_solution(radius, total_mass_);
  }
  return interior_solution(equation_of_state, radius, mass_over_radius(radius),
                           log_specific_enthalpy(radius),
                           log_lapse_at_outer_radius_);
}

template <>
RelativisticEuler::Solutions::TovStar<TovSolution>::RadialVariables<DataVector>
TovSolution::radial_variables(
    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
    const tnsr::I<DataVector, 3>& x) const noexcept {
  // add small number to avoid FPEs at origin
  const DataVector radius = get(magnitude(x)) + 1.e-30 * outer_radius_;
  if (min(radius) >= outer_radius_) {
    return vacuum_solution(radius, total_mass_);
  }
  if (max(radius) <= outer_radius_) {
    DataVector mass_over_radius_data(radius.size());
    DataVector log_of_specific_enthalpy(radius.size());
    for (size_t i = 0; i < get_size(radius); i++) {
      const double r = get_element(radius, i);
      get_element(mass_over_radius_data, i) = mass_over_radius(r);
      get_element(log_of_specific_enthalpy, i) = log_specific_enthalpy(r);
    }
    return interior_solution(equation_of_state, radius, mass_over_radius_data,
                             log_of_specific_enthalpy,
                             log_lapse_at_outer_radius_);
  }
  RelativisticEuler::Solutions::TovStar<TovSolution>::RadialVariables<
      DataVector>
      result(radius);
  for (size_t i = 0; i < radius.size(); i++) {
    const double r = radius[i];
    auto radial_vars_at_r =
        (r <= outer_radius_
             ? interior_solution(equation_of_state, r, mass_over_radius(r),
                                 log_specific_enthalpy(r),
                                 log_lapse_at_outer_radius_)
             : vacuum_solution(r, total_mass_));
    get(result.rest_mass_density)[i] = get(radial_vars_at_r.rest_mass_density);
    get(result.pressure)[i] = get(radial_vars_at_r.pressure);
    get(result.specific_internal_energy)[i] =
        get(radial_vars_at_r.specific_internal_energy);
    get(result.specific_enthalpy)[i] = get(radial_vars_at_r.specific_enthalpy);
    result.metric_time_potential[i] = radial_vars_at_r.metric_time_potential;
    result.dr_metric_time_potential[i] =
        radial_vars_at_r.dr_metric_time_potential;
    result.metric_radial_potential[i] =
        radial_vars_at_r.metric_radial_potential;
    result.dr_metric_radial_potential[i] =
        radial_vars_at_r.dr_metric_radial_potential;
  }
  result.metric_angular_potential = make_with_value<DataVector>(radius, 0.0);
  result.dr_metric_angular_potential = make_with_value<DataVector>(radius, 0.0);
  return result;
}

void TovSolution::pup(PUP::er& p) noexcept {  // NOLINT
  p | outer_radius_;
  p | total_mass_;
  p | log_lapse_at_outer_radius_;
  p | mass_over_radius_interpolant_;
  p | log_enthalpy_interpolant_;
}

}  // namespace gr::Solutions


//
// Copyright 2005-2007 Adobe Systems Incorporated
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
#ifndef BOOST_GIL_EXTENSION_NUMERIC_ALGORITHM_HPP
#define BOOST_GIL_EXTENSION_NUMERIC_ALGORITHM_HPP

#include <boost/gil/extension/numeric/pixel_numeric_operations.hpp>

#include <boost/gil/metafunctions.hpp>
#include <boost/gil/pixel_iterator.hpp>

#include <boost/assert.hpp>

#include <algorithm>
#include <iterator>
#include <numeric>
#include <type_traits>

namespace boost { namespace gil {

/// \brief Reference proxy associated with a type that has a \p "reference" member type alias.
///
/// The reference proxy is the reference type, but with stripped-out C++ reference.
/// Models PixelConcept.
template <typename T>
struct pixel_proxy : std::remove_reference<typename T::reference> {};

/// \brief std::for_each for a pair of iterators
template <typename Iterator1, typename Iterator2, typename BinaryFunction>
BinaryFunction for_each(Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryFunction f)
{
    while (first1 != last1)
        f(*first1++, *first2++);
    return f;
}

template <typename SrcIterator, typename DstIterator>
inline
auto assign_pixels(SrcIterator src, SrcIterator src_end, DstIterator dst) -> DstIterator
{
    for_each(src, src_end, dst,
        pixel_assigns_t
        <
            typename pixel_proxy<typename std::iterator_traits<SrcIterator>::value_type>::type,
            typename pixel_proxy<typename std::iterator_traits<DstIterator>::value_type>::type
        >());
    return dst + (src_end - src);
}

namespace detail {

template <std::size_t Size>
struct inner_product_k_t
{
    template
    <
        class InputIterator1,
        class InputIterator2,
        class T,
        class BinaryOperation1,
        class BinaryOperation2
    >
    static T apply(
        InputIterator1 first1,
        InputIterator2 first2, T init,
        BinaryOperation1 binary_op1,
        BinaryOperation2 binary_op2)
    {
        init = binary_op1(init, binary_op2(*first1, *first2));
        return inner_product_k_t<Size - 1>::template apply(
            first1 + 1, first2 + 1, init, binary_op1, binary_op2);
    }
};

template <>
struct inner_product_k_t<0>
{
    template
    <
        class InputIterator1,
        class InputIterator2,
        class T,
        class BinaryOperation1,
        class BinaryOperation2
    >
    static T apply(
        InputIterator1 first1,
        InputIterator2 first2,
        T init,
        BinaryOperation1 binary_op1,
        BinaryOperation2 binary_op2)
    {
        return init;
    }
};

} // namespace detail

/// static version of std::inner_product
template
<
    std::size_t Size,
    class InputIterator1,
    class InputIterator2,
    class T,
    class BinaryOperation1,
    class BinaryOperation2
>
BOOST_FORCEINLINE
T inner_product_k(
    InputIterator1 first1,
    InputIterator2 first2,
    T init,
    BinaryOperation1 binary_op1,
    BinaryOperation2 binary_op2)
{
    return detail::inner_product_k_t<Size>::template apply(
        first1, first2, init, binary_op1, binary_op2);
}

/// \brief 1D un-guarded cross-correlation with a variable-size kernel
template
<
    typename PixelAccum,
    typename SrcIterator,
    typename KernelIterator,
    typename Size,
    typename DstIterator
>
inline
auto correlate_pixels_n(
    SrcIterator src_begin,
    SrcIterator src_end,
    KernelIterator kernel_begin,
    Size kernel_size,
    DstIterator dst_begin)
    -> DstIterator
{
    using src_pixel_ref_t = typename pixel_proxy
        <
            typename std::iterator_traits<SrcIterator>::value_type
        >::type;
    using dst_pixel_ref_t = typename pixel_proxy
        <
            typename std::iterator_traits<DstIterator>::value_type
        >::type;
    using kernel_value_t = typename std::iterator_traits<KernelIterator>::value_type;

    PixelAccum accum_zero;
    pixel_zeros_t<PixelAccum>()(accum_zero);
    while (src_begin != src_end)
    {
        pixel_assigns_t<PixelAccum, dst_pixel_ref_t>()(
            std::inner_product(
                src_begin,
                src_begin + kernel_size,
                kernel_begin,
                accum_zero,
                pixel_plus_t<PixelAccum, PixelAccum, PixelAccum>(),
                pixel_multiplies_scalar_t<src_pixel_ref_t, kernel_value_t, PixelAccum>()),
            *dst_begin);

        ++src_begin;
        ++dst_begin;
    }
    return dst_begin;
}

/// \brief 1D un-guarded cross-correlation with a fixed-size kernel
template
<
    std::size_t Size,
    typename PixelAccum,
    typename SrcIterator,
    typename KernelIterator,
    typename DstIterator
>
inline
auto correlate_pixels_k(
    SrcIterator src_begin,
    SrcIterator src_end,
    KernelIterator kernel_begin,
    DstIterator dst_begin)
    -> DstIterator
{
    using src_pixel_ref_t = typename pixel_proxy
        <
            typename std::iterator_traits<SrcIterator>::value_type
        >::type;
    using dst_pixel_ref_t = typename pixel_proxy
        <
            typename std::iterator_traits<DstIterator>::value_type
        >::type;
    using kernel_type = typename std::iterator_traits<KernelIterator>::value_type;

    PixelAccum accum_zero;
    pixel_zeros_t<PixelAccum>()(accum_zero);
    while (src_begin != src_end)
    {
        pixel_assigns_t<PixelAccum, dst_pixel_ref_t>()(
            inner_product_k<Size>(
                src_begin,
                kernel_begin,
                accum_zero,
                pixel_plus_t<PixelAccum, PixelAccum, PixelAccum>(),
                pixel_multiplies_scalar_t<src_pixel_ref_t, kernel_type, PixelAccum>()),
            *dst_begin);

        ++src_begin;
        ++dst_begin;
    }
    return dst_begin;
}

/// \brief destination is set to be product of the source and a scalar
/// \tparam PixelAccum - TODO
/// \tparam SrcView Models ImageViewConcept
/// \tparam DstView Models MutableImageViewConcept
template <typename PixelAccum, typename SrcView, typename Scalar, typename DstView>
inline
void view_multiplies_scalar(SrcView const& src_view, Scalar const& scalar, DstView const& dst_view)
{
    static_assert(std::is_scalar<Scalar>::value, "Scalar is not scalar");
    BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
    using src_pixel_ref_t = typename pixel_proxy<typename SrcView::value_type>::type;
    using dst_pixel_ref_t = typename pixel_proxy<typename DstView::value_type>::type;
    using y_coord_t = typename SrcView::y_coord_t;

    y_coord_t const height = src_view.height();
    for (y_coord_t y = 0; y < height; ++y)
    {
        typename SrcView::x_iterator it_src = src_view.row_begin(y);
        typename DstView::x_iterator it_dst = dst_view.row_begin(y);
        typename SrcView::x_iterator it_src_end = src_view.row_end(y);
        while (it_src != it_src_end)
        {
            pixel_assigns_t<PixelAccum, dst_pixel_ref_t>()(
                pixel_multiplies_scalar_t<src_pixel_ref_t, Scalar, PixelAccum>()(*it_src, scalar),
                *it_dst);

            ++it_src;
            ++it_dst;
        }
    }
}

}} // namespace boost::gil

#endif


#ifndef VIGRA_EXPORT_GRAPH_ALGORITHM_VISITOR_HXX
#define VIGRA_EXPORT_GRAPH_ALGORITHM_VISITOR_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/multi_watersheds.hxx>
namespace python = boost::python;

namespace vigra{

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

    friend class def_visitor_access;

    typedef GRAPH Graph;

    typedef LemonGraphAlgorithmVisitor<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<EdgeMapDim,   Singleband<UInt32> > UInt32EdgeArray;
    typedef NumpyArray<EdgeMapDim,   Singleband<Int32 > > Int32EdgeArray;
    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 NumpyArray<EdgeMapDim +1,Multiband <float > > MultiFloatEdgeArray;

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

    typedef ShortestPathDijkstra<Graph,float> ShortestPathDijkstraType;

    typedef typename GraphDescriptorToMultiArrayIndex<Graph>::IntrinsicNodeMapShape NodeCoordinate;
    typedef NumpyArray<1,NodeCoordinate>  NodeCoorinateArray;

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

    }

    void exportSegmentationAlgorithms()const{
        python::def("_edgeWeightedWatershedsSegmentation",registerConverters(&pyEdgeWeightedWatershedsSegmentation),
            (
                python::arg("graph"),
                python::arg("edgeWeights"),
                python::arg("seeds"),
                python::arg("out")=python::object()
            ),
            "Seeded watersheds on a edge weighted graph"
        );

        python::def("_nodeWeightedWatershedsSegmentation",registerConverters(&pyNodeWeightedWatershedsSegmentation),
            (
                python::arg("graph"),
                python::arg("nodeWeights"),
                python::arg("seeds"),
                python::arg("method")=std::string("regionGrowing"),
                python::arg("out")=python::object()
            ),
            "Seeded watersheds on a node weighted graph"
        );
        python::def("_nodeWeightedWatershedsSeeds",registerConverters(&pyNodeWeightedWatershedsSeeds),
            (
                python::arg("graph"),
                python::arg("nodeWeights"),
                python::arg("out")=python::object()
            ),
            "Generate seeds for node weighted watersheds"
        );

        python::def("_carvingSegmentation",registerConverters(&pyCarvingSegmentation),
            (
                python::arg("graph"),
                python::arg("edgeWeights"),
                python::arg("seeds"),
                python::arg("backgroundLabel"),
                python::arg("backgroundBias"),
                python::arg("noBiasBelow") = 0.0,
                python::arg("out")=python::object()
            ),
            "Seeded watersheds on a edge weighted graph"
        );

        python::def("_shortestPathSegmentation",registerConverters(&pyShortestPathSegmentation),
            (
                python::arg("graph"),
                python::arg("edgeWeights"),
                python::arg("nodeWeights"),
                python::arg("seeds"),
                python::arg("out")=python::object()
            ),
            "Seeded shorted path segmentation on a edge and node weighted graph"
        );

        python::def("_felzenszwalbSegmentation",registerConverters(&pyFelzenszwalbSegmentation),
            (
                python::arg("graph"),
                python::arg("edgeWeights"),
                python::arg("nodeSizes"),
                python::arg("k")=300.0f,
                python::arg("nodeNumStop")=-1,
                python::arg("out")=python::object()
            ),
            "Felzenwalb graph based segmentation"
        );
    }

    void exportMiscAlgorithms()const{

        python::def("_nodeFeatureDistToEdgeWeight",registerConverters(&pyNodeFeatureDistToEdgeWeight),
            (
                python::arg("graph"),
                python::arg("nodeFeatures"),
                python::arg("metric"),
                python::arg("out")=python::object()
            ),
            "convert node features to edge weights with the given metric"
        );
        python::def("_nodeFeatureSumToEdgeWeight",registerConverters(&pyNodeFeatureSumToEdgeWeight),
            (
                python::arg("graph"),
                python::arg("nodeFeatures"),
                python::arg("out")=python::object()
            ),
            "convert node features to edge weights"
        );

        python::def("_opengmMulticutDataStructure",registerConverters(&pyMulticutDataStructure),
            (
                python::arg("graph"),
                python::arg("edgeWeights")
            )
        );

        python::def("nodeGtToEdgeGt",registerConverters(&pyNodeGtToEdgeGt),
            (
                python::arg("graph"),
                python::arg("nodeGt"),
                python::arg("ignoreLabel"),
                python::arg("out")=python::object()
            )
        );

        python::def("_opengmArgToLabeling",registerConverters(&pyMulticutArgToLabeling),
            (
                python::arg("graph"),
                python::arg("arg"),
                python::arg("out")=python::object()
            )
        );
        python::def("_wardCorrection",registerConverters(&pyWardCorrection),
            (
                python::arg("graph"),
                python::arg("edgeIndicator"),
                python::arg("nodeSize"),
                python::arg("out")=python::object()
            ),
            "apply wards method to an edgeIndicator"
        );
    }

    void exportSmoothingAlgorithms()const{

        python::def("_recursiveGraphSmoothing",registerConverters(&pyRecursiveGraphSmoothing),
            (
                python::arg("graph"),
                python::arg("nodeFeatures"),
                python::arg("edgeIndicator"),
                python::arg("gamma"),
                python::arg("edgeThreshold"),
                python::arg("scale"),
                python::arg("iterations")=1,
                python::arg("outBuffer")=python::object(),
                python::arg("out")=python::object()
            ),
            "recursive edge weighted guided graph smoothing"
        );

    }

    std::string clsName_;
    template <class classT>
    void visit(classT& c) const
    {
        // - watersheds-segmentation
        // - carving-segmentation
        // - felzenwalb-segmentation
        // - labeling
        exportSegmentationAlgorithms();

        // - node Labels (usefull to make grid graph labels from rag labels)
        // - node feature distance to edge weights
        exportMiscAlgorithms();

        // - recursiveGraphSmoothing
        exportSmoothingAlgorithms();
    }

    static NumpyAnyArray pyWardCorrection(
        const Graph &           g,
        const FloatEdgeArray    edgeWeightsArray,
        const FloatNodeArray    nodeSizeArray,
        const float             wardness,
        FloatEdgeArray    outArray
    ){
        outArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g));

        // numpy arrays => lemon maps
        FloatEdgeArrayMap  edgeWeightsArrayMap(g,edgeWeightsArray);
        FloatNodeArrayMap  nodeSizeArrayMap(g,nodeSizeArray);
        FloatEdgeArrayMap  outArrayMap(g,outArray);

        for(EdgeIt iter(g);iter!=lemon::INVALID;++iter){
            const float uSize=nodeSizeArrayMap[g.u(*iter)];
            const float vSize=nodeSizeArrayMap[g.v(*iter)];
            const float w = edgeWeightsArrayMap[*iter];
            const float ward  = 1.0f/(1.0f/std::log(uSize) + 1.0f/std::log(vSize)  );
            const float wardF = wardness*ward + (1.0-wardness);
            outArrayMap[*iter]=w*wardF;
        }
        return outArray;

    }

    static python::tuple pyMulticutDataStructure(
        const Graph &           g,
        const FloatEdgeArray    edgeWeightsArray
    ){
        UInt32NodeArray toDenseArray( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g));

        // numpy arrays => lemon maps
        UInt32NodeArrayMap toDenseArrayMap(g,toDenseArray);
        FloatEdgeArrayMap  edgeWeightsArrayMap(g,edgeWeightsArray);

        NumpyArray<2,UInt32> vis      ((    typename NumpyArray<2,UInt64>::difference_type(g.edgeNum(),2)));
        NumpyArray<1,float > weights  ((    typename NumpyArray<1,double>::difference_type(g.edgeNum()  )));

        size_t denseIndex = 0 ;
        for(NodeIt iter(g);iter!=lemon::INVALID;++iter){
            toDenseArrayMap[*iter]=denseIndex;
            ++denseIndex;
        }
        denseIndex=0;
        for(EdgeIt iter(g);iter!=lemon::INVALID;++iter){
            const size_t dU=toDenseArrayMap[g.u(*iter)];
            const size_t dV=toDenseArrayMap[g.v(*iter)];
            vis(denseIndex,0)=std::min(dU,dV);
            vis(denseIndex,1)=std::max(dU,dV);
            weights(denseIndex)=edgeWeightsArrayMap[*iter];
            ++denseIndex;
        }
        return python::make_tuple(vis,weights);

    }

    static NumpyAnyArray pyNodeGtToEdgeGt(
        const Graph &           g,
        const UInt32NodeArray & nodeGt,
        const Int64 ignoreLabel,
        UInt32EdgeArray edgeGt
    ){
        edgeGt.reshapeIfEmpty(IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g));

        // numpy arrays => lemon maps
        UInt32NodeArrayMap nodeGtMap(g,nodeGt);
        UInt32EdgeArrayMap  edgeGtMap(g,edgeGt);
        nodeGtToEdgeGt(g, nodeGtMap, ignoreLabel, edgeGtMap);
        return edgeGt;
    }

    static NumpyAnyArray pyMulticutArgToLabeling(
        const Graph &              g,
        const NumpyArray<1,UInt32> arg,
        UInt32NodeArray            labelsArray
    ){
        labelsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g));
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);
        size_t denseIndex = 0 ;
        for(NodeIt iter(g);iter!=lemon::INVALID;++iter){
            labelsArrayMap[*iter]=arg(denseIndex);
            ++denseIndex;
        }
        return labelsArray;
    }
    /*
    static NumpyAnyArray pyNodeIdsLabels(
        const GRAPH & g,
        NumpyArray<1,Singleband<UInt32> >  nodeIds,
        UInt32NodeArray                    nodeLabelArray,
        NumpyArray<1,Singleband<UInt32> >  out
    ){
        // reshape out
        out.reshapeIfEmpty(nodeIds.shape());

        // numpy arrays => lemon maps
        UInt32NodeArrayMap nodeLabelArrayMap(g,nodeLabelArray);

        for(size_t i=0;i<nodeIds.shape(0);++i)
            out(i)=nodeLabelArrayMap[g.nodeFromId(nodeIds(i))];
        return out;
    }

    static NumpyAnyArray pyNodeIdsFeatures(
        const GRAPH & g,
        NumpyArray<1,Singleband<UInt32> >  nodeIds,
        MultiFloatNodeArray                nodeFeaturesArray,
        NumpyArray<2,Multiband<float >  >  out
    ){
        //  reshape out ?
        typename NumpyArray<2,Multiband<float> >::difference_type outShape(nodeIds.shape(0),nodeFeaturesArray.shape(NodeMapDim));
        out.reshapeIfEmpty(  NumpyArray<2,Multiband<float >  >::ArrayTraits::taggedShape(outShape,"xc"));

        // numpy arrays => lemon maps
        MultiFloatNodeArrayMap nodeFeaturesArrayMap(g,nodeFeaturesArray);

        typedef typename  NumpyArray<1,int>::difference_type Coord1;
        for(size_t i=0;i<nodeIds.shape(0);++i)
            out[Coord1(i)]=nodeFeaturesArrayMap[g.nodeFromId(nodeIds(i))];
        return out;
    }
    */
    static NumpyAnyArray pyNodeFeatureDistToEdgeWeight(
        const GRAPH & g,
        const MultiFloatNodeArray & nodeFeaturesArray,
        const std::string & functor,
        FloatEdgeArray edgeWeightsArray
    ){
        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        if(functor==std::string("euclidean") || functor==std::string("norm") || functor==std::string("l2")){
            typedef  metrics::Norm<float> DistFunctor;
            DistFunctor f;
            return pyNodeFeatureDistToEdgeWeightT<DistFunctor>(g,nodeFeaturesArray,f,edgeWeightsArray);
        }
        if(functor==std::string("squaredNorm")){
            typedef  metrics::SquaredNorm<float> DistFunctor;
            DistFunctor f;
            return pyNodeFeatureDistToEdgeWeightT<DistFunctor>(g,nodeFeaturesArray,f,edgeWeightsArray);
        }
        else if (functor==std::string("manhattan") || functor==std::string("l1")){
            typedef  metrics::Manhattan<float> DistFunctor;
            DistFunctor f;
            return pyNodeFeatureDistToEdgeWeightT<DistFunctor>(g,nodeFeaturesArray,f,edgeWeightsArray);
        }
        else if (functor==std::string("chiSquared")){
            typedef  metrics::ChiSquared<float> DistFunctor;
            DistFunctor f;
            return pyNodeFeatureDistToEdgeWeightT<DistFunctor>(g,nodeFeaturesArray,f,edgeWeightsArray);
        }
        else{
            throw std::runtime_error(
                "distance not supported\n"
                "supported distance types:\n"
                "- euclidean/norm/l2\n"
                "- squaredNorm\n"
                "- manhattan/l1\n"
                "- chiSquared\n"
            );
        }
    }

    static NumpyAnyArray pyNodeFeatureSumToEdgeWeight(
        const GRAPH & g,
        const FloatNodeArray & nodeFeaturesArray,
        FloatEdgeArray edgeWeightsArray
    ){
        // reshape out?
        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        // numpy arrays => lemon maps
        FloatNodeArrayMap  nodeFeatureArrayMap(g,nodeFeaturesArray);
        FloatEdgeArrayMap  edgeWeightsArrayMap(g,edgeWeightsArray);

        for(EdgeIt e(g);e!=lemon::INVALID;++e){
            const Edge edge(*e);
            const Node u=g.u(edge);
            const Node v=g.v(edge);
            edgeWeightsArrayMap[edge]=nodeFeatureArrayMap[u]+nodeFeatureArrayMap[v];
        }
        return edgeWeightsArray;
    }

    template<class FUNCTOR>
    static NumpyAnyArray pyNodeFeatureDistToEdgeWeightT(
        const GRAPH & g,
        const MultiFloatNodeArray & nodeFeaturesArray,
        FUNCTOR & functor,
        FloatEdgeArray edgeWeightsArray
    ){
        // reshape out?
        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        // numpy arrays => lemon maps
        MultiFloatNodeArrayMap nodeFeatureArrayMap(g,nodeFeaturesArray);
        FloatEdgeArrayMap      edgeWeightsArrayMap(g,edgeWeightsArray);

        for(EdgeIt e(g);e!=lemon::INVALID;++e){
            const Edge edge(*e);
            const Node u=g.u(edge);
            const Node v=g.v(edge);
            edgeWeightsArrayMap[edge]=functor(nodeFeatureArrayMap[u],nodeFeatureArrayMap[v]);
        }
        return edgeWeightsArray;
    }

    static NumpyAnyArray pyEdgeWeightedWatershedsSegmentation(
        const GRAPH & g,
        FloatEdgeArray edgeWeightsArray,
        UInt32NodeArray seedsArray,
        UInt32NodeArray labelsArray
    ){
        // resize output ?
        labelsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        UInt32NodeArrayMap seedsArrayMap(g,seedsArray);
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);

        // call algorithm itself
        edgeWeightedWatershedsSegmentation(g,edgeWeightsArrayMap,seedsArrayMap,labelsArrayMap);

        // retun labels
        return labelsArray;
    }

    static NumpyAnyArray pyNodeWeightedWatershedsSegmentation(
        const Graph &       g,
        FloatNodeArray      nodeWeightsArray,
        UInt32NodeArray     seedsArray,
        const std::string & method,
        UInt32NodeArray     labelsArray
    ){

        // resize output ?
        labelsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        WatershedOptions watershedsOption;
        if(method==std::string("regionGrowing"))
            watershedsOption.regionGrowing();
        else
            watershedsOption.unionFind();

        // numpy arrays => lemon maps
        FloatNodeArrayMap  nodeWeightsArrayMap(g,nodeWeightsArray);
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);

        std::copy(seedsArray.begin(),seedsArray.end(),labelsArray.begin());

        //lemon_graph::graph_detail::generateWatershedSeeds(g, nodeWeightsArrayMap, labelsArrayMap, watershedsOption.seed_options);
        lemon_graph::watershedsGraph(g, nodeWeightsArrayMap, labelsArrayMap, watershedsOption);
        //lemon_graph::graph_detail::seededWatersheds(g, nodeWeightsArrayMap, seedsArrayMap, watershedsOption);

        return labelsArray;
    }

    static NumpyAnyArray pyNodeWeightedWatershedsSeeds(
        const Graph &       g,
        FloatNodeArray      nodeWeightsArray,
        UInt32NodeArray     seedsArray
    ){
        const std::string method="regionGrowing";
        // resize output ?
        seedsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        WatershedOptions watershedsOption;
        if(method==std::string("regionGrowing"))
            watershedsOption.regionGrowing();

        // numpy arrays => lemon maps
        FloatNodeArrayMap  nodeWeightsArrayMap(g,nodeWeightsArray);
        UInt32NodeArrayMap seedsArrayMap(g,seedsArray);

        lemon_graph::graph_detail::generateWatershedSeeds(g, nodeWeightsArrayMap, seedsArrayMap, watershedsOption.seed_options);

        return seedsArray;
    }

    static NumpyAnyArray pyCarvingSegmentation(
        const GRAPH & g,
        FloatEdgeArray edgeWeightsArray,
        UInt32NodeArray seedsArray,
        const UInt32    backgroundLabel,
        const float     backgroundBias,
        const float     noBiasBelow,
        UInt32NodeArray labelsArray
    ){
        // resize output ?
        labelsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        UInt32NodeArrayMap seedsArrayMap(g,seedsArray);
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);

        // call algorithm itself
        carvingSegmentation(g,edgeWeightsArrayMap,seedsArrayMap,backgroundLabel,backgroundBias,noBiasBelow,labelsArrayMap);

        // retun labels
        return labelsArray;
    }

    static NumpyAnyArray pyShortestPathSegmentation(
        const Graph &       g,
        FloatEdgeArray      edgeWeightsArray,
        FloatNodeArray      nodeWeightsArray,
        UInt32NodeArray     seedsArray,
        UInt32NodeArray     labelsArray
    ){

        // resize output ?
        labelsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap  edgeWeightsArrayMap(g,edgeWeightsArray);
        FloatNodeArrayMap  nodeWeightsArrayMap(g,nodeWeightsArray);
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);

        std::copy(seedsArray.begin(),seedsArray.end(),labelsArray.begin());

        shortestPathSegmentation<
            Graph,FloatEdgeArrayMap, FloatNodeArrayMap, UInt32NodeArrayMap, float
        >(g, edgeWeightsArrayMap, nodeWeightsArrayMap, labelsArrayMap);

        return labelsArray;
    }

    static NumpyAnyArray pyFelzenszwalbSegmentation(
        const GRAPH & g,
        FloatEdgeArray edgeWeightsArray,
        FloatNodeArray nodeSizesArray,
        const float k,
        const int nodeNumStop,
        UInt32NodeArray labelsArray
    ){
        // resize output ?
        labelsArray.reshapeIfEmpty(  IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap  edgeWeightsArrayMap(g,edgeWeightsArray);
        FloatNodeArrayMap  nodeSizesArrayMap(g,nodeSizesArray);
        UInt32NodeArrayMap labelsArrayMap(g,labelsArray);

        // call algorithm itself
        felzenszwalbSegmentation(g,edgeWeightsArrayMap,nodeSizesArrayMap,k,labelsArrayMap,nodeNumStop);

        // retun labels
        return labelsArray;
    }

    static NumpyAnyArray pyRecursiveGraphSmoothing(
        const GRAPH & g,
        MultiFloatNodeArray nodeFeaturesArray,
        FloatEdgeArray      edgeIndicatorArray,
        const float         lambda,
        const float         edgeThreshold,
        const float         scale,
        const size_t        iterations,
        MultiFloatNodeArray nodeFeaturesBufferArray,
        MultiFloatNodeArray nodeFeaturesOutArray
    ){
        TaggedShape inShape  = nodeFeaturesArray.taggedShape();
        TaggedShape outShape = TaggedGraphShape<Graph>::taggedNodeMapShape(g);
        if(inShape.hasChannelAxis()){
            outShape.setChannelCount(inShape.channelCount());
        }
        nodeFeaturesBufferArray.reshapeIfEmpty(outShape);
        nodeFeaturesOutArray.reshapeIfEmpty(outShape);

        // numpy arrays => lemon maps
        MultiFloatNodeArrayMap nodeFeaturesArrayMap(g,nodeFeaturesArray);
        FloatEdgeArrayMap edgeIndicatorArrayMap(g,edgeIndicatorArray);
        MultiFloatNodeArrayMap nodeFeaturesBufferArrayMap(g,nodeFeaturesBufferArray);
        MultiFloatNodeArrayMap nodeFeaturesOutArrayMap(g,nodeFeaturesOutArray);

        // call algorithm itself
        recursiveGraphSmoothing(g,nodeFeaturesArrayMap,edgeIndicatorArrayMap,lambda,edgeThreshold,scale,iterations,nodeFeaturesBufferArrayMap,nodeFeaturesOutArrayMap);

        // retun smoothed features
        return nodeFeaturesOutArray;
    }

};

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

    friend class def_visitor_access;

    typedef GRAPH Graph;

    typedef LemonGraphAlgorithmVisitor<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 NumpyArray<EdgeMapDim +1,Multiband <float > > MultiFloatEdgeArray;

    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 NumpyMultibandEdgeMap<Graph,MultiFloatEdgeArray> MultiFloatEdgeArrayMap;

    typedef ShortestPathDijkstra<Graph,float> ShortestPathDijkstraType;

    typedef typename GraphDescriptorToMultiArrayIndex<Graph>::IntrinsicNodeMapShape NodeCoordinate;
    typedef NumpyArray<1,NodeCoordinate>  NodeCoorinateArray;

    LemonGridGraphAlgorithmAddonVisitor(const std::string & clsName){}

    template <class classT>
    void visit(classT& c) const
    {

        // - edge weights from interpolated image
        exportMiscAlgorithms(c);

    }

    template <class classT>
    void exportMiscAlgorithms(classT & c)const{

        python::def("edgeFeaturesFromInterpolatedImage",registerConverters(&pyEdgeWeightsFromInterpolatedImage),
            (
                python::arg("graph"),
                python::arg("image"),
                python::arg("out")=python::object()
            ),
            "convert an image with with ``shape = graph.shape*2 - 1`` to an edge weight array"
        );

        python::def("edgeFeaturesFromImage",registerConverters(&pyEdgeWeightsFromImage),
            (
                python::arg("graph"),
                python::arg("image"),
                python::arg("out")=python::object()
            ),
            "convert an image with with shape = graph.shape OR shape = graph.shape *2 -1 to an edge weight array"
        );

        python::def("edgeFeaturesFromImage",registerConverters(&pyEdgeWeightsFromImageMb),
            (
                python::arg("graph"),
                python::arg("image"),
                python::arg("out")=python::object()
            ),
            "convert an image with with shape = graph.shape OR shape = graph.shape *2 -1 to an edge weight array"
        );

        c
        .def("affiliatedEdgesSerializationSize",&pyAffiliatedEdgesSerializationSize,
            (
                python::arg("rag"),
                python::arg("affiliatedEdges")
            )
        );

        //'python::def("edgeFeaturesFromInterpolatedImageCorrected",registerConverters(&pyEdgeWeightsFromInterpolatedImageCorrected),
        //'    (
        //'        python::arg("graph"),
        //'        python::arg("image"),
        //'        python::arg("out")=python::object()
        //'    ),
        //'    "convert an image with with shape = graph.shape *2 -1 to an edge weight array"
        //'    ""
        //');
    }

    static size_t pyAffiliatedEdgesSerializationSize(
        const GRAPH & gridGraph,
        const AdjacencyListGraph & rag,
        const typename AdjacencyListGraph:: template EdgeMap< std::vector<Edge> > & affiliatedEdges
    ){
        return affiliatedEdgesSerializationSize(gridGraph, rag, affiliatedEdges);
    }

    static NumpyAnyArray pyEdgeWeightsFromImage(
        const GRAPH & g,
        const FloatNodeArray & image,
        FloatEdgeArray edgeWeightsArray
    ){

        bool regularShape=true;
        bool topologicalShape=true;

        for(size_t d=0;d<NodeMapDim;++d){
            if(image.shape(d)!=g.shape()[d]){
                regularShape=false;
            }
            if(image.shape(d)!=2*g.shape()[d]-1){
                topologicalShape=false;
            }
        }

        if(regularShape)
            return pyEdgeWeightsFromOrginalSizeImage(g,image,edgeWeightsArray);
        else if(topologicalShape)
            return pyEdgeWeightsFromInterpolatedImage(g,image,edgeWeightsArray);
        else{
            vigra_precondition(false, "shape of edge image does not match graph shape");
            // to avid no return warnings
            return pyEdgeWeightsFromOrginalSizeImage(g,image,edgeWeightsArray);
        }
    }

    static NumpyAnyArray pyEdgeWeightsFromImageMb(
        const GRAPH & g,
        const MultiFloatNodeArray & image,
        MultiFloatEdgeArray edgeWeightsArray
    ){

        bool regularShape=true;
        bool topologicalShape=true;

        for(size_t d=0;d<NodeMapDim;++d){
            if(image.shape(d)!=g.shape()[d]){
                regularShape=false;
            }
            if(image.shape(d)!=2*g.shape()[d]-1){
                topologicalShape=false;
            }
        }

        if(regularShape)
            return pyEdgeWeightsFromOrginalSizeImageMb(g,image,edgeWeightsArray);
        else if(topologicalShape)
            return pyEdgeWeightsFromInterpolatedImageMb(g,image,edgeWeightsArray);
        else{
            vigra_precondition(false, "shape of edge image does not match graph shape");
            // to avid no return warnings
            return pyEdgeWeightsFromOrginalSizeImageMb(g,image,edgeWeightsArray);
        }
    }

    static NumpyAnyArray pyEdgeWeightsFromInterpolatedImage(
        const GRAPH & g,
        const FloatNodeArray & interpolatedImage,
        FloatEdgeArray edgeWeightsArray
    ){

        for(size_t d=0;d<NodeMapDim;++d){
            //std::cout<<"is "<<interpolatedImage.shape(d)<<"gs "<<2*g.shape()[d]-1<<"\n";
            vigra_precondition(interpolatedImage.shape(d)==2*g.shape()[d]-1, "interpolated shape must be shape*2 -1");
        }
        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        typedef typename FloatNodeArray::difference_type CoordType;
        for(EdgeIt iter(g); iter!=lemon::INVALID; ++ iter){

            const Edge edge(*iter);
            const CoordType uCoord(g.u(edge));
            const CoordType vCoord(g.v(edge));
            const CoordType tCoord = uCoord+vCoord;
            edgeWeightsArrayMap[edge]=interpolatedImage[tCoord];
        }
        return edgeWeightsArray;
    }

    static NumpyAnyArray pyEdgeWeightsFromOrginalSizeImage(
        const GRAPH & g,
        const FloatNodeArray & image,
        FloatEdgeArray edgeWeightsArray
    ){

        for(size_t d=0;d<NodeMapDim;++d){
            //std::cout<<"is "<<image.shape(d)<<"gs "<<2*g.shape()[d]-1<<"\n";
            vigra_precondition(image.shape(d)==g.shape()[d], "interpolated shape must be shape*2 -1");
        }
        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        typedef typename FloatNodeArray::difference_type CoordType;
        for(EdgeIt iter(g); iter!=lemon::INVALID; ++ iter){

            const Edge edge(*iter);
            const CoordType uCoord(g.u(edge));
            const CoordType vCoord(g.v(edge));
            edgeWeightsArrayMap[edge]=(image[uCoord]+image[vCoord])/2.0;
        }
        return edgeWeightsArray;
    }

    static NumpyAnyArray pyEdgeWeightsFromInterpolatedImageMb(
        const GRAPH & g,
        const MultiFloatNodeArray & interpolatedImage,
        MultiFloatEdgeArray edgeWeightsArray
    ){

        for(size_t d=0;d<NodeMapDim;++d){
            //std::cout<<"is "<<interpolatedImage.shape(d)<<"gs "<<2*g.shape()[d]-1<<"\n";
            vigra_precondition(interpolatedImage.shape(d)==2*g.shape()[d]-1, "interpolated shape must be shape*2 -1");
        }

        // resize out
        typename MultiArray<EdgeMapDim+1,int>::difference_type outShape;
        for(size_t d=0;d<EdgeMapDim;++d){
            outShape[d]=IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g)[d];
        }
        outShape[EdgeMapDim] = interpolatedImage.shape(NodeMapDim);

        edgeWeightsArray.reshapeIfEmpty(   MultiFloatEdgeArray::ArrayTraits::taggedShape(outShape,"nc") );

        // numpy arrays => lemon maps
        MultiFloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        typedef typename FloatNodeArray::difference_type CoordType;
        for(EdgeIt iter(g); iter!=lemon::INVALID; ++ iter){

            const Edge edge(*iter);
            const CoordType uCoord(g.u(edge));
            const CoordType vCoord(g.v(edge));
            const CoordType tCoord = uCoord+vCoord;
            edgeWeightsArrayMap[edge]=interpolatedImage[tCoord];
        }
        return edgeWeightsArray;
    }

    static NumpyAnyArray pyEdgeWeightsFromOrginalSizeImageMb(
        const GRAPH & g,
        const MultiFloatNodeArray & image,
        MultiFloatEdgeArray edgeWeightsArray
    ){

        for(size_t d=0;d<NodeMapDim;++d){
            //std::cout<<"is "<<image.shape(d)<<"gs "<<2*g.shape()[d]-1<<"\n";
            vigra_precondition(image.shape(d)==g.shape()[d], "interpolated shape must be shape*2 -1");
        }

        // resize out
        typename MultiArray<EdgeMapDim+1,int>::difference_type outShape;
        for(size_t d=0;d<EdgeMapDim;++d){
            outShape[d]=IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g)[d];
        }
        outShape[EdgeMapDim] = image.shape(NodeMapDim);

        //edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(outShape),"ec" );

        edgeWeightsArray.reshapeIfEmpty(   MultiFloatEdgeArray::ArrayTraits::taggedShape(outShape,"nc") );

        // numpy arrays => lemon maps
        MultiFloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        typedef typename FloatNodeArray::difference_type CoordType;
        for(EdgeIt iter(g); iter!=lemon::INVALID; ++ iter){

            const Edge edge(*iter);
            const CoordType uCoord(g.u(edge));
            const CoordType vCoord(g.v(edge));
            MultiArray<1, float>  val = image[uCoord];
            val+=image[vCoord];
            val/=2.0;
            edgeWeightsArrayMap[edge]=val;
        }
        return edgeWeightsArray;
    }

    /*
    static NumpyAnyArray pyEdgeWeightsFromInterpolatedImageCorrected(
        const GRAPH & g,
        const FloatNodeArray & interpolatedImage,
        FloatEdgeArray edgeWeightsArray
    ){

        for(size_t d=0;d<NodeMapDim;++d){
            //std::cout<<"is "<<interpolatedImage.shape(d)<<"gs "<<2*g.shape()[d]-1<<"\n";
            vigra_precondition(interpolatedImage.shape(d)==2*g.shape()[d]-1, "interpolated shape must be shape*2 -1");
        }

        edgeWeightsArray.reshapeIfEmpty( IntrinsicGraphShape<Graph>::intrinsicEdgeMapShape(g) );

        // numpy arrays => lemon maps
        FloatEdgeArrayMap edgeWeightsArrayMap(g,edgeWeightsArray);
        typedef typename FloatNodeArray::difference_type CoordType;
        for(EdgeIt iter(g); iter!=lemon::INVALID; ++ iter){

            const Edge edge(*iter);
            const CoordType uCoord(g.u(edge));
            const CoordType vCoord(g.v(edge));
            const CoordType tCoord = uCoord+vCoord;
            int diffCounter = 0;
            for(int i=0; i<NodeMapDim; ++i) {
                if (uCoord[i] != vCoord[i]) {
                    diffCounter++;
                }
            }
            edgeWeightsArrayMap[edge]=sqrt(diffCounter)*interpolatedImage[tCoord];
        }
        return edgeWeightsArray;
    }
    */
};

} // end namespace vigra

#endif // VIGRA_EXPORT_GRAPH_ALGORITHM_VISITOR_HXX