#include <doctest/doctest.h>  // for ResultBuilder, TestCase, CHECK

#include <boost/utility/string_view.hpp>  // for boost::string_view
#include <chrono>                         // for duration, operator-, steady_clock
#include <ckpttn/MLPartMgr.hpp>           // for MLPartMgr
#include <cstdint>                        // for uint8_t
#include <iostream>                       // for operator<<, basic_ostream, endl, cout
#include <ratio>                          // for ratio
#include <vector>                         // for vector

#include "ckpttn/PartMgrBase.hpp"  // for SimpleNetlist
#include "ckpttn/netlist.hpp"      // for Netlist

template <typename Gnl> class FMBiConstrMgr;
template <typename Gnl> class FMBiGainMgr;
template <typename Gnl> class FMKWayConstrMgr;
template <typename Gnl> class FMKWayGainMgr;
template <typename Gnl, typename GainMgr, typename ConstrMgr> class FMPartMgr;

using namespace std;

// using experimental::randint;

extern auto create_test_netlist() -> SimpleNetlist;  // import create_test_netlist
extern auto create_dwarf() -> SimpleNetlist;         // import create_dwarf
extern auto readNetD(boost::string_view netDFileName) -> SimpleNetlist;
extern void readAre(SimpleNetlist& H, boost::string_view areFileName);

TEST_CASE("Test MLBiPartMgr dwarf") {
    const auto H = create_dwarf();
    MLPartMgr partMgr{0.3};
    vector<uint8_t> part(H.number_of_modules(), 0);
    partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMBiGainMgr<SimpleNetlist>,
                                                     FMBiConstrMgr<SimpleNetlist>>>(H, part);
    CHECK(partMgr.totalcost == 2U);
}

TEST_CASE("Test MLKWayPartMgr dwarf") {
    const auto H = create_dwarf();
    MLPartMgr partMgr{0.4, 3};  // 0.3???
    vector<uint8_t> part(H.number_of_modules(), 0);
    partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMKWayGainMgr<SimpleNetlist>,
                                                     FMKWayConstrMgr<SimpleNetlist>>>(H, part);
    CHECK(partMgr.totalcost == 4U);
}

TEST_CASE("Test MLBiPartMgr p1") {
    const auto H = readNetD("../../testcases/p1.net");
    MLPartMgr partMgr{0.3};
    partMgr.set_limitsize(500);

    auto mincost = 1000;
    for (auto i = 0; i != 10; ++i) {
        auto part = vector<uint8_t>(H.number_of_modules(), 0);
        auto whichPart = 0U;
        for (auto& elem : part) {
            whichPart ^= 1;
            elem = uint8_t(whichPart);
        }
        partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMBiGainMgr<SimpleNetlist>,
                                                         FMBiConstrMgr<SimpleNetlist>>>(H, part);
        if (mincost > partMgr.totalcost) {
            mincost = partMgr.totalcost;
        }
    }
    // CHECK(partMgr.totalcost >= 50);
    // CHECK(partMgr.totalcost <= 50);
    CHECK(mincost >= 29U);
    CHECK(mincost <= 73U);
}

TEST_CASE("Test MLBiPartMgr ibm01") {
    auto H = readNetD("../../testcases/ibm01.net");
    readAre(H, "../../testcases/ibm01.are");
    MLPartMgr partMgr{0.4};
    partMgr.set_limitsize(400);

    auto mincost = 1000;
    for (auto i = 0; i != 10; ++i) {
        auto part = vector<uint8_t>(H.number_of_modules(), 0);
        auto whichPart = 0U;
        for (auto& elem : part) {
            whichPart ^= 1;
            elem = uint8_t(whichPart);
        }
        partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMBiGainMgr<SimpleNetlist>,
                                                         FMBiConstrMgr<SimpleNetlist>>>(H, part);
        if (mincost > partMgr.totalcost) {
            mincost = partMgr.totalcost;
        }
    }
    // CHECK(partMgr.totalcost >= 650);
    // CHECK(partMgr.totalcost <= 650);
    CHECK(mincost >= 221U);
    CHECK(mincost <= 695U);
}

TEST_CASE("Test MLBiPartMgr ibm03") {
    auto H = readNetD("../../testcases/ibm03.net");
    readAre(H, "../../testcases/ibm03.are");
    MLPartMgr partMgr{0.45};
    partMgr.set_limitsize(300);
    vector<uint8_t> part(H.number_of_modules(), 0);
    // auto part_info = PartInfo{move(part), py::set<node_t>()};
    auto begin = chrono::steady_clock::now();
    partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMBiGainMgr<SimpleNetlist>,
                                                     FMBiConstrMgr<SimpleNetlist>>>(H, part);
    chrono::duration<double> last = chrono::steady_clock::now() - begin;
    cout << "time: " << last.count() << endl;
    CHECK(partMgr.totalcost >= 1104U);
    CHECK(partMgr.totalcost <= 2041U);
}

TEST_CASE("Test MLBiPartMgr ibm18") {
    auto H = readNetD("../../testcases/ibm18.net");
    readAre(H, "../../testcases/ibm18.are");
    MLPartMgr partMgr{0.45};
    partMgr.set_limitsize(24000);
    vector<uint8_t> part(H.number_of_modules(), 0);
    // auto part_info = PartInfo{move(part), py::set<node_t>()};
    auto begin = chrono::steady_clock::now();
    partMgr.run_FMPartition<SimpleNetlist, FMPartMgr<SimpleNetlist, FMBiGainMgr<SimpleNetlist>,
                                                     FMBiConstrMgr<SimpleNetlist>>>(H, part);
    chrono::duration<double> last = chrono::steady_clock::now() - begin;
    cout << "time: " << last.count() << endl;
    CHECK(partMgr.totalcost >= 1104U);
    CHECK(partMgr.totalcost <= 5112U);
}

/*

Advantages:

1. Python-like, networkx
2. Check legalization, report
3. Generic
4. K buckets rather than K - 1
5. Time, space, and code complexity.
6. Design issues

*/


#include <boost/type_traits/is_complex.hpp>
#include <complex>

#ifndef FLOAT_PRECISION
using data_t = double;
#else
using data_t = float;
#endif

namespace fhmdot {
namespace utils {

template <typename T> constexpr bool is_complex() {
  return boost::is_complex<T>::value;
}

template <typename T> constexpr bool is_float() {
  if (is_complex<T>()) {
    return std::is_same<typename std::complex<float>, T>::value;
  } else {
    return std::is_same<float, T>::value;
  }
}

template <typename T> constexpr char num_character() {
  if (std::is_same<float, T>::value) {
    return 'F';
  } else if (std::is_same<double, T>::value) {
    return 'D';
  } else if (std::is_same<typename std::complex<float>, T>::value) {
    return 'C';
  } else if (std::is_same<typename std::complex<double>, T>::value) {
    return 'Z';
  } else {
    return '?';
  };
};

} // namespace utils

} // namespace fhmdot


//==================================================================================================
/*!
  @file

  @copyright 2017 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ALGORITHM_TRANSFORM_REDUCE_HPP_INCLUDED
#define BOOST_SIMD_ALGORITHM_TRANSFORM_REDUCE_HPP_INCLUDED

#include <boost/simd/range/segmented_aligned_range.hpp>
#include <boost/simd/function/aligned_load.hpp>
#include <boost/simd/function/load.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/detail/is_aligned.hpp>
#include <boost/simd/meta/cardinal_of.hpp>

namespace boost { namespace simd
{
  /*!
    @ingroup group-algo

    Applies unary_op to each element in the range [first; last) and reduces the results
    (possibly permuted and aggregated in unspecified manner) along with the initial value init over binary_op.

    @param first  Beginning of the range of elements to transform
    @param last   End of the range of elements to transform
    @param unop   unary operation function object that will be applied.
    @param init   the initial value of the reduction
    @param binop  binary operation function object that will be applied.

    @par Requirement

      - @c first, @c last must be pointer to Vectorizable type.

      - @c unop and binop must be a polymorphic unary function object, i.e callable on generic types,
        at least @c T and  @c boost::simd::pack<T>
      - returns the generalized sum of init and unary_op(*first), unary_op(*(first+1)), . unary_op(*(last-1)) over binary_op,

      where generalized sum GSUM(op, a1, ., aN) is defined as follows:

      if N=1, a1
      if N > 1, op(GSUM(op, b1, ., bK), GSUM(op, bM, ., bN)) where

        - b1, ., bN may be any permutation of a1, ., aN and
        - 1 < K+1 = M <= N

      in other words, the results of unary_op may be grouped and arranged in arbitrary order.
      a pointer to the element past the last element transformed.

    @par Note:

      - unop is not applied to init

      - If the range is empty, init is returned, unmodified

    @par Example:

      The following code uses simd::transform to compute sum of the square of the values stored in a
      @c std::vector.

      @snippet transform_reduce.cpp transform_reduce

    @par possible output:

       @snippet transform_reduce.txt transform_reduce

  **/
  template<typename T, typename Unop, typename Binop>
  T transform_reduce(T const* first, T const* last
                    , Unop unop, T init, Binop binop)
  {
    auto pr = segmented_aligned_range(first,last);

    for( auto const& x : pr.head ) init = binop(init, unop(x));
    auto b = pr.body.begin();
    auto e = pr.body.end();
    if (b != e)
    {
      pack<T> acc(unop(*b++));
      for( ; b != e; b++) acc  = binop(acc , unop(*b));
      for( T     x : acc) init = binop(init, x);
    }
    for( auto const& x : pr.tail ) init = binop(init, unop(x));

    return init;
  }

  /*!
    @ingroup group-algo

    Applies transform binary oerator  to each element in the range [first1; last1) and [first2, )
    and reduces the results (possibly permuted and aggregated in unspecified manner)
    along with the initial value init over generalized sum gsum binary operator.

    @param first1      Beginning of the first range of elements to transform
    @param last1       End of the first range of elements to transform
    @param first2      Beginning of the second range of elements to transform
    @param transform   binary operation function object that will be applied.
    @param init        the initial value of the reduction
    @param gsum        binary operation function object that will be applied.

    @par Requirement

      - T, U and V must be types which can be used within
      - @c transform and gsum must be a polymorphic unary function object, i.e callable on generic types,
        at least
          - transform must be able to be called on couples @c (T, U) and  @c (boost::simd::pack<T>, boost::simd::pack<U>)
          to produce respectively @c V and boost::simd::pack<V> types
          - gsum must be able to be called on V and boost::simd::pack<V> types to produce the same output type.
      - returns the generalized sum of init and transform(*first1, *first2), transform(*(first+1), *(first2+1)), .,
        transform(*(last1-1), *(first2-(last1-first1)-1)) over binary_op,

      where generalized sum GSUM(op, a1, ., aN) is defined as follows:

      if N=1, a1
      if N > 1, op(GSUM(op, b1, ., bK), GSUM(op, bM, ., bN)) where

        - b1, ., bN may be any permutation of a1, ., aN and
        - 1 < K+1 = M <= N

      in other words, the results of transform may be grouped and arranged in arbitrary order.
      a pointer to the element past the last element transformed.

    @par Note:

      - If the range is empty, init is returned, unmodified

    @par Example:

      The following code uses simd::transform to compute the dot-product of the values stored in two
      @c std::vector.

      @snippet transform_reduce2.cpp transform_reduce2

    @par possible output:

       @snippet transform_reduce2.txt transform_reduce2

  **/
  template<typename T, typename U, typename V, typename Binop1, typename Binop2>
  V transform_reduce(T const* first1, T const* last1, U const* first2
                    , Binop1 transform, V init, Binop2 gsum)
  {
    using vU = pack<U>;
    using vV = pack<V>;
    using vT = pack<T>;
    static_assert ( bs::cardinal_of_t<vT>::value == bs::cardinal_of_t<vU>::value
                  , "SIMD cardinal mismatch between T and U"
                  );
    static_assert (  bs::cardinal_of_t<vT>::value == bs::cardinal_of_t<vV>::value
                  , "SIMD cardinal mismatch between T and V"
                  );
    auto pr = segmented_aligned_range(first1,last1);

    for( auto const& x : pr.head ){
      init = gsum(init, transform(x, *first2++));
    }

    // main SIMD part
    auto b = pr.body.begin();
    auto e = pr.body.end();
    if (b !=  e)
    {
      if( boost::simd::detail::is_aligned(first2 , vU::alignment) ) // first2 is aligned
      {
        vV acc(transform(vT(*b++), aligned_load<vU>(first2)));
        first2+= vU::static_size;
        for( ; b != e; b++, first2+= vU::static_size){
          acc  = gsum(acc, transform(vT(*b), aligned_load<vU>(first2)));
        }
        for( V x : acc) init = gsum(init, x);
      }
      else
      {
        vV acc(transform(vT(*b++), load<vU>(first2)));
        first2+= vU::static_size;
        for( ; b != e; b++, first2+= vU::static_size){
          acc  = gsum(acc, transform(vT(*b), load<vU>(first2)));
        }
        for( V x : acc) init = gsum(init, x);
      }
    }

    for( auto const& x : pr.tail )
      init = gsum(init, transform(x, *first2++));

    return init;
  }

} }

#endif


// Copyright 2008 Gunter Winkler <guwi17@gmx.de>
// Thanks to Tiago Requeijo for providing this test
// 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)

#include <iostream>
#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/numeric/ublas/triangular.hpp>
#include <boost/cstdlib.hpp>

using namespace std;
namespace ublas = boost::numeric::ublas;

int main()
{
    int sz = 4;
    ublas::symmetric_matrix<int, ublas::upper, ublas::column_major>  UpCol (sz, sz);
    ublas::symmetric_matrix<int, ublas::upper, ublas::row_major>     UpRow (sz, sz);
    ublas::symmetric_matrix<int, ublas::lower, ublas::column_major>  LoCol (sz, sz);
    ublas::symmetric_matrix<int, ublas::lower, ublas::row_major>     LoRow (sz, sz);

    ublas::triangular_matrix<int, ublas::upper, ublas::column_major>  TrUpCol (sz, sz);
    ublas::triangular_matrix<int, ublas::upper, ublas::row_major>     TrUpRow (sz, sz);
    ublas::triangular_matrix<int, ublas::lower, ublas::column_major>  TrLoCol (sz, sz);
    ublas::triangular_matrix<int, ublas::lower, ublas::row_major>     TrLoRow (sz, sz);

    for(int i=0; i<sz; ++i)
        for(int j=i; j<sz; ++j)
        {
            // Symmetric
            UpCol(i,j) = 10*i + j;
            UpRow(i,j) = 10*i + j;
            LoCol(i,j) = 10*i + j;
            LoRow(i,j) = 10*i + j;
            // Triangular
            TrUpCol(i,j) = 10*i + j;
            TrUpRow(i,j) = 10*i + j;
            TrLoCol(j,i) = 10*i + j;
            TrLoRow(j,i) = 10*i + j;
        }

    //get pointers to data
    int* uc = &(UpCol.data()[0]);
    int* ur = &(UpRow.data()[0]);
    int* lc = &(LoCol.data()[0]);
    int* lr = &(LoRow.data()[0]);
    int* tuc = &(TrUpCol.data()[0]);
    int* tur = &(TrUpRow.data()[0]);
    int* tlc = &(TrLoCol.data()[0]);
    int* tlr = &(TrLoRow.data()[0]);

    // upper, column_major
    //   storage should be:  0 1 11 2 12 22 3 13 23 33
    int uc_correct[] = {0, 1, 11, 2, 12, 22, 3, 13, 23, 33};

    // upper, row_major
    //   storage should be:  0 1 2 3 11 12 13 22 23 33
    int ur_correct[] = {0, 1, 2, 3, 11, 12, 13, 22, 23, 33};

    // lower, column_major
    //   storage should be:  0 1 2 3 11 12 13 22 23 33
    int lc_correct[] = {0, 1, 2, 3, 11, 12, 13, 22, 23, 33};

    // lower, row_major
    //   storage should be:  0 1 11 2 12 22 3 13 23 33
    int lr_correct[] = {0, 1, 11, 2, 12, 22, 3, 13, 23, 33};

    bool success = true;

    // Test Symmetric
    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(uc[i] != uc_correct[i])
        {
            cout << "Storage error (Symmetric, Upper, Column major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(ur[i] != ur_correct[i])
        {
            cout << "Storage error (Symmetric, Upper, Row major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(lc[i] != lc_correct[i])
        {
            cout << "Storage error (Symmetric, Lower, Column major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(lr[i] != lr_correct[i])
        {
            cout << "Storage error (Symmetric, Lower, Row major)" << endl;
            success = false;
            break;
        }

    // Test Triangular
    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(tuc[i] != uc_correct[i])
        {
            cout << "Storage error (Triangular, Upper, Column major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(tur[i] != ur_correct[i])
        {
            cout << "Storage error (Triangular, Upper, Row major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(tlc[i] != lc_correct[i])
        {
            cout << "Storage error (Triangular, Lower, Column major)" << endl;
            success = false;
            break;
        }

    for(int i=0; i<sz*(sz+1)/2; ++i)
        if(tlr[i] != lr_correct[i])
        {
            cout << "Storage error (Triangular, Lower, Row major)" << endl;
            success = false;
            break;
        }

        return (success)?boost::exit_success:boost::exit_failure;
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <typeinfo>
#include <boost/numeric/mtl/mtl.hpp>

//#include <boost/numeric/mtl/matrix/hermitian_view.hpp>

using namespace std;

template <typename Matrix>
void test_type(Matrix)
{

    cout << "Type " << typeid(Matrix).name()
	 << "\n     has ashape " << typeid(typename mtl::ashape::ashape<Matrix>::type).name() << '\n';
    cout << "Type " << typeid(const Matrix).name()
	 << "\n     has ashape " << typeid(typename mtl::ashape::ashape<const Matrix>::type).name() << '\n';
}

template <typename Matrix>
void test(const Matrix&, const char* name)
{
    cout << name
	    // << " ... " << typeid(typename mtl::OrientedCollection<mtl::mat::hermitian_view<Matrix> >::orientation).name()
	 << '\n';

    cout << name << '\n';
    Matrix A(3, 3), B(3, 3), C(3, 3);
    // Stupid test with real values --> use complex
    A= 2, 3, 4,
       1, 2, 3,
       3, 4, 5;
    B= 3, 4, 5,
       2, 3, 4,
       1, 2, 3;

    C= hermitian(B);
    cout << "hermitian(B) is\n" << C;

    C= A * hermitian(B);
    cout << "A * hermitian(B) is\n" << C;

    typedef typename mtl::Collection<Matrix>::value_type value_type;
    mtl::dense_vector<value_type> v(3), w(3, 1.0);

#if 0 // understanding ashape of const views
    const Matrix K(A);
    test_type(K);
    test_type(trans(K));
    test_type(conj(K));
    test_type(conj(trans(K)));
    test_type(trans(conj(K)));
#endif

    v= hermitian(A) * w;
    v= mtl::mat::conj(trans(A)) * w;
    v= trans(mtl::mat::conj(A)) * w;
    v= trans(A) * w;
}

int main(int, char**)
{
    using namespace mtl;
    dense2D<double>                                      dr;
    dense2D<double, mat::parameters<col_major> >      dc;
    morton_dense<double, recursion::morton_z_mask>       mzd;
    morton_dense<double, recursion::doppled_2_row_mask>  d2r;
    compressed2D<double>                                 cr;
    compressed2D<double, mat::parameters<col_major> > cc;

    dense2D<complex<double> >                            drc;
    compressed2D<complex<double> >                       crc;

    test(dr, "Dense row major");
#if 0
    test(dc, "Dense column major");
    test(mzd, "Morton Z-order");
    test(d2r, "Hybrid 2 row-major");
    test(cr, "Compressed row major");
    test(drc, "Dense row major complex");

    // For better readability I don't want finish with a complex
    test(cc, "Compressed column major");
#endif
    return 0;
}


// (C) Copyright Andrew Sutton 2007
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0 (See accompanying file
// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GRAPH_GEODESIC_DISTANCE_HPP
#define BOOST_GRAPH_GEODESIC_DISTANCE_HPP

#include <boost/graph/detail/geodesic.hpp>
#include <boost/graph/exterior_property.hpp>

namespace boost
{
template <typename Graph,
          typename DistanceType,
          typename ResultType,
          typename Divides = std::divides<ResultType> >
struct mean_geodesic_measure
    : public geodesic_measure<Graph, DistanceType, ResultType>
{
    typedef geodesic_measure<Graph, DistanceType, ResultType> base_type;
    typedef typename base_type::distance_type distance_type;
    typedef typename base_type::result_type result_type;

    result_type operator ()(distance_type d, const Graph& g)
    {
        function_requires< VertexListGraphConcept<Graph> >();
        function_requires< NumericValueConcept<DistanceType> >();
        function_requires< NumericValueConcept<ResultType> >();
        function_requires< AdaptableBinaryFunctionConcept<Divides,ResultType,ResultType,ResultType> >();

        return (d == base_type::infinite_distance())
            ? base_type::infinite_result()
            : div(result_type(d), result_type(num_vertices(g) - 1));
    }
    Divides div;
};

template <typename Graph, typename DistanceMap>
inline mean_geodesic_measure<Graph, typename property_traits<DistanceMap>::value_type, double>
measure_mean_geodesic(const Graph&, DistanceMap)
{
    return mean_geodesic_measure<Graph, typename property_traits<DistanceMap>::value_type, double>();
}

template <typename T, typename Graph, typename DistanceMap>
inline mean_geodesic_measure<Graph, typename property_traits<DistanceMap>::value_type, T>
measure_mean_geodesic(const Graph&, DistanceMap)
{
    return mean_geodesic_measure<Graph, typename property_traits<DistanceMap>::value_type, T>();
}

// This is a little different because it's expected that the result type
// should (must?) be the same as the distance type. There's a type of
// transitivity in this thinking... If the average of distances has type
// X then the average of x's should also be type X. Is there a case where this
// is not true?
//
// This type is a little under-genericized... It needs generic parameters
// for addition and division.
template <typename Graph, typename DistanceType>
struct mean_graph_distance_measure
    : public geodesic_measure<Graph, DistanceType, DistanceType>
{
    typedef geodesic_measure<Graph, DistanceType, DistanceType> base_type;
    typedef typename base_type::distance_type distance_type;
    typedef typename base_type::result_type result_type;

    inline result_type operator ()(distance_type d, const Graph& g)
    {
        function_requires< VertexListGraphConcept<Graph> >();
        function_requires< NumericValueConcept<DistanceType> >();

        if(d == base_type::infinite_distance()) {
            return base_type::infinite_result();
        }
        else {
            return d / result_type(num_vertices(g));
        }
    }
};

template <typename Graph, typename DistanceMap>
inline mean_graph_distance_measure<Graph, typename property_traits<DistanceMap>::value_type>
measure_graph_mean_geodesic(const Graph& g, DistanceMap dist)
{
    typedef typename property_traits<DistanceMap>::value_type T;
    return mean_graph_distance_measure<Graph, T>();
}

template <typename Graph,
          typename DistanceMap,
          typename Measure,
          typename Combinator>
inline typename Measure::result_type
mean_geodesic(const Graph& g,
                DistanceMap dist,
                Measure measure,
                Combinator combine)
{
    function_requires< DistanceMeasureConcept<Measure,Graph> >();
    typedef typename Measure::distance_type Distance;

    Distance n = detail::combine_distances(g, dist, combine, Distance(0));
    return measure(n, g);
}

template <typename Graph,
            typename DistanceMap,
            typename Measure>
inline typename Measure::result_type
mean_geodesic(const Graph& g, DistanceMap dist, Measure measure)
{
    function_requires< DistanceMeasureConcept<Measure,Graph> >();
    typedef typename Measure::distance_type Distance;

    return mean_geodesic(g, dist, measure, std::plus<Distance>());
}

template <typename Graph, typename DistanceMap>
inline double
mean_geodesic(const Graph& g, DistanceMap dist)
{ return mean_geodesic(g, dist, measure_mean_geodesic(g, dist)); }

template <typename T, typename Graph, typename DistanceMap>
inline T
mean_geodesic(const Graph& g, DistanceMap dist)
{ return mean_geodesic(g, dist, measure_mean_geodesic<T>(g, dist)); }

template <typename Graph,
            typename DistanceMatrixMap,
            typename GeodesicMap,
            typename Measure>
inline typename property_traits<GeodesicMap>::value_type
all_mean_geodesics(const Graph& g,
                    DistanceMatrixMap dist,
                    GeodesicMap geo,
                    Measure measure)
{
    function_requires< VertexListGraphConcept<Graph> >();
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename graph_traits<Graph>::vertex_iterator VertexIterator;
    function_requires< ReadablePropertyMapConcept<DistanceMatrixMap,Vertex> >();
    typedef typename property_traits<DistanceMatrixMap>::value_type DistanceMap;
    function_requires< DistanceMeasureConcept<Measure,Graph> >();
    typedef typename Measure::result_type Result;
    function_requires< WritablePropertyMapConcept<GeodesicMap,Vertex> >();
    function_requires< NumericValueConcept<Result> >();

    // NOTE: We could compute the mean geodesic here by performing additional
    // computations (i.e., adding and dividing). However, I don't really feel
    // like fully genericizing the entire operation yet so I'm not going to.

    Result inf = numeric_values<Result>::infinity();
    Result sum = numeric_values<Result>::zero();
    VertexIterator i, end;
    for(tie(i, end) = vertices(g); i != end; ++i) {
        DistanceMap dm = get(dist, *i);
        Result r = mean_geodesic(g, dm, measure);
        put(geo, *i, r);

        // compute the sum along with geodesics
        if(r == inf) {
            sum = inf;
        }
        else if(sum != inf) {
            sum += r;
        }
    }

    // return the average of averages.
    return sum / Result(num_vertices(g));
}

template <typename Graph, typename DistanceMatrixMap, typename GeodesicMap>
inline typename property_traits<GeodesicMap>::value_type
all_mean_geodesics(const Graph& g, DistanceMatrixMap dist, GeodesicMap geo)
{
    function_requires< GraphConcept<Graph> >();
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
    function_requires< ReadablePropertyMapConcept<DistanceMatrixMap,Vertex> >();
    typedef typename property_traits<DistanceMatrixMap>::value_type DistanceMap;
    function_requires< WritablePropertyMapConcept<GeodesicMap,Vertex> >();
    typedef typename property_traits<GeodesicMap>::value_type Result;

    return all_mean_geodesics(g, dist, geo, measure_mean_geodesic<Result>(g, DistanceMap()));
}

template <typename Graph, typename GeodesicMap, typename Measure>
inline typename Measure::result_type
small_world_distance(const Graph& g, GeodesicMap geo, Measure measure)
{
    function_requires< DistanceMeasureConcept<Measure,Graph> >();
    typedef typename Measure::result_type Result;

    Result sum = detail::combine_distances(g, geo, std::plus<Result>(), Result(0));
    return measure(sum, g);
}

template <typename Graph, typename GeodesicMap>
inline typename property_traits<GeodesicMap>::value_type
small_world_distance(const Graph& g, GeodesicMap geo)
{ return small_world_distance(g, geo, measure_graph_mean_geodesic(g, geo)); }

}

#endif


#include <boost/test/unit_test.hpp>
#include <vector>
#include <numeric>
#include <limits>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <unordered_map>

using namespace std;
using namespace boost;

// go through each element and check whether it is incremented and return range

struct Range
{
	int Start;
	int End;

	Range( int s, int e ) : Start( s), End(e) {}
	Range( int s) : Start( s), End(s) {}
};

typedef boost::shared_ptr<Range> RangePtr;

class ConsecutiveNumbers
{
public:
	list< RangePtr> FindConsecutives( int numbers[], int numbersCount )
	{
		list<RangePtr> ranges;

		if( numbersCount == 0 )
			return ranges;

		int index = 0;
		RangePtr r( new Range( numbers[index++] ) );

		for( ; index<numbersCount; index++ )
		{
			if( r->End >= numbers[index] )
			{
				throw "Should have numbers in ascending order";
			}

			if( r->End+1 == numbers[index] )
			{
				r->End = numbers[index];
			}
			else
			{
				ranges.push_back( r );

				r = RangePtr( new Range( numbers[index] ) );
			}
		}

		ranges.push_back( r );
		return ranges;
	}
};

BOOST_AUTO_TEST_CASE( TestQuiz5 )
{
	int numbers[] = { 4, 5, 6, 7, 8, 9, 12, 15, 16, 17, 18, 20, 22, 23, 24, 27 };

	ConsecutiveNumbers finder;
	list<RangePtr> ranges =
		finder.FindConsecutives( numbers, sizeof( numbers ) / sizeof( numbers[0] ) );

}


//! Implementation of the input_parser.

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <stack>
#include <algorithm>
#include <Eigen/Dense>
#include "../include/ply.h"
#include "../include/input_parser.h"

//! The subscript of the laminate code. e.g. [0/45/45/90]8s4. 8 is the pre_count,
//! s is the has_symmetry, and 4 is the post_count.
struct SubscriptInfo {
    int pre_count;
    bool has_symmetry;
    int post_count;
};

//! Read material_data file and return the format into a map.
std::map<std::string, Properties>
    load_material_data(const std::string& filename);

//! Parse the laminate code and return a pair in which the first elemnt is the
//! vector containing ply angles, and the second element is the SubscriptInfo
//! of the given laminate.
std::pair<std::vector<double>, SubscriptInfo>
    laminate_code_parser(std::string laminate_code);

// Parse the trailing info of the laminate code and return the info into the
// SubscriptInfo struct.
SubscriptInfo subscript_parser(std::string subscript);

//! Convert the laminate information into a vector of ply objects.
std::vector<ply> build_laminate_vector(
    const std::pair<std::vector<double>, SubscriptInfo>& layout_info,
    std::vector<std::string>& ply_materials,
    const std::vector<double>& ply_thickness,
    const std::map<std::string, Properties>& material_map);

//! Strip the left and right square bracket of a string.
std::string strip_bracket(std::string input_str);

//! Convert the string inside bracket into vector of doubles.
std::vector<double> strs_to_vector(std::string code);

//! Convert the string inside bracket into vector of strings. Used for the
//! material data parsing.
std::vector<std::string> mat_str_to_vector(std::string strings);

using std::string;
using std::cin; using std::cout; using std::endl;
using std::vector; using std::map; using std::stack; using std::pair;

vector<string> read_composite_input(const string& filename) {
    vector<string> result;
    string line;
    std::ifstream file(filename);
    if (file.is_open()) {
        while (getline(file, line)) {
            if(line.find("[") != string::npos &&
                line.find("]") != string::npos) {
                string::size_type l_bracket_pos = line.find("[");
                result.push_back(line.substr(l_bracket_pos));
            }
        }
    }
    return result;
}

vector<ply> get_ply_vector(vector<string>& input_strings,
    const string& material_data_filename) {

        map<string, Properties> material_data =
            load_material_data(material_data_filename);

        pair<vector<double>, SubscriptInfo> layout_info =
            laminate_code_parser(input_strings[0]);

        string material_strings = strip_bracket(input_strings[1]);
        vector<string> ply_materials =
            mat_str_to_vector(material_strings);

        string thickness_strings = strip_bracket(input_strings[2]);

        vector<double> ply_thickness =
            strs_to_vector(thickness_strings);

        vector<ply> laminate = build_laminate_vector(layout_info,
            ply_materials, ply_thickness, material_data);

    return laminate;
    }

Eigen::Matrix<double, 6, 1> get_load_vector(string& input_string) {
    string load_string = strip_bracket(input_string);
    vector<double> load_stl_vector = strs_to_vector(load_string);
    Eigen::Matrix<double, 6, 1> load_vector(load_stl_vector.data());
    return load_vector;

}

double get_minimum_ply_thickness(string& thickness_strings_with_brackets) {
    string thickness_strings = strip_bracket(thickness_strings_with_brackets);
    vector<double> ply_thickness =
        strs_to_vector(thickness_strings);
    auto min_it = std::min_element(ply_thickness.begin(),
                                    ply_thickness.end());

    double minimum = *min_it;

    return minimum;
}

map<string, Properties> load_material_data(const string& filename) {
    map<string, Properties> data;
    string line;
    std::ifstream input_file(filename);
    if (input_file.is_open()) {
        string material_name;
        Properties p;
        input_file.ignore(500, '\n');  // Ignore the first line
        while (!input_file.eof()) {
            input_file >> material_name >> p.E1 >> p.E2 >> p.nu12 >> p.G12;
            if (!input_file) {
                cout << "Error: file corrupted. Lines After "
                    "corrupted line are not read."<< endl;
                break;
            }
            data.insert({material_name, p});
        }
    } else {
        cout << "Error: Cannot open file." ;
    }
    return data;
}

string strip_bracket(string input_str) {
    std::size_t left_bracket_pos = input_str.find("[");
    std::size_t right_bracket_pos = input_str.find("]");
    return input_str.substr(left_bracket_pos+1,
        right_bracket_pos-left_bracket_pos-1);

}

pair<vector<double>, SubscriptInfo> laminate_code_parser(string laminate_code) {
    std::size_t left_bracket_pos = laminate_code.find("[");
    std::size_t right_bracket_pos = laminate_code.find("]");
    pair<vector<double>, SubscriptInfo> result;
    if (left_bracket_pos == string::npos || right_bracket_pos == string::npos
        || left_bracket_pos > right_bracket_pos) {
            cout << "Invalid laminate code input." << endl;
        return result;
    }
    string theta_string = laminate_code.substr(left_bracket_pos+1,
        right_bracket_pos-left_bracket_pos-1);
    string subscript_string = laminate_code.substr(right_bracket_pos+1,
        laminate_code.size()-right_bracket_pos-1);
    result.first = strs_to_vector(theta_string);
    result.second = subscript_parser(subscript_string);
    return result;
}

vector<ply> build_laminate_vector(
    const pair<vector<double>, SubscriptInfo>& layout_info,
    vector<string>& ply_materials,
    const vector<double>& ply_thickness,
    const std::map<string, Properties>& material_map) {

        vector<double> theta_vec = layout_info.first;
        SubscriptInfo info = layout_info.second;

        vector<ply> laminate_vec;
        stack<ply> ply_stack;

        // It's not optimized in that we don't need the stack operation if
        // we don't have the symmetry subscript, but this arrangement
        // makes the code less redundant.
        for (int i = 1; i <= info.pre_count; i++) {
            auto theta_it = theta_vec.begin();
            auto mat_it = ply_materials.begin();
            auto t_it = ply_thickness.begin();
            for (; theta_it != theta_vec.end(); theta_it++, mat_it++, t_it++) {
                ply local_ply(*mat_it, material_map.at(*mat_it),
                    *theta_it, *t_it);
                laminate_vec.push_back(local_ply);
                ply_stack.push(local_ply); // for symmetric condition
            }
        }

        if (info.has_symmetry) {
            while (!ply_stack.empty()) {
                laminate_vec.push_back(ply_stack.top());
                ply_stack.pop();
            }
        }

        if (info.post_count > 1) {
            vector<ply> laminate_copy(laminate_vec);
            for (int i = 2; i <= info.post_count; i++) {
                for (auto it = laminate_copy.begin();
                    it != laminate_copy.end(); it++) {
                    laminate_vec.push_back(*it);
                }

            }
        }

    return laminate_vec;

}

std::vector<double> strs_to_vector(string theta_string) {
    std::replace(theta_string.begin(), theta_string.end(), '/', ' ');
    std::replace(theta_string.begin(), theta_string.end(), ',', ' ');
    std::istringstream theta_stream(theta_string);
    std::vector<double> theta_vec;
    double theta;
    while (theta_stream >> theta) {
        theta_vec.push_back(theta);
    }

    return theta_vec;
}

std::vector<string> mat_str_to_vector(string strings) {
    std::replace(strings.begin(), strings.end(), ',', ' ');
    std::istringstream stream(strings);
    std::vector<string> vec;
    string single_string;
    while (stream >> single_string) {
        vec.push_back(single_string);
    }
    return vec;
}

SubscriptInfo subscript_parser(string subscript) {
    SubscriptInfo info;
    std::size_t s_pos = subscript.find("s");
    if (s_pos != string::npos) {  // subscript contains an 's'
        info.has_symmetry = true;

        if (s_pos != 0) {  // subscript not at the beginning
            string pre_repetition_string = subscript.substr(0, s_pos);
            if (std::stoi(pre_repetition_string) > 0) {
                info.pre_count = std::stoi(pre_repetition_string);
            } else {
                info.pre_count = 1;
            }
        } else {
            info.pre_count = 1;
        }

        if (s_pos != subscript.size() - 1) {  // subscript not at the end
            string post_repetition_string =
                subscript.substr(s_pos+1, subscript.size());
            info.post_count = std::stoi(post_repetition_string);
        } else {
            info.post_count = 1;
        }

    } else {
        string pre_repetition_string = subscript.substr(0, subscript.size());
        info.pre_count = std::stoi(pre_repetition_string);
        info.has_symmetry = false;
        info.post_count = 0;
    }
    return info;
}


#include "plugin_graph_algorithm.h"

#include "core/log.h"

#include "netlist/gate.h"
#include "netlist/net.h"
#include "netlist/netlist.h"

#include <boost/graph/graphviz.hpp>
#include <boost/graph/strong_components.hpp>

std::set<std::set<std::shared_ptr<gate>>> plugin_graph_algorithm::get_strongly_connected_components(std::shared_ptr<netlist> g, std::set<std::shared_ptr<gate>> gates)
{
    if (g == nullptr)
    {
        log_error(this->get_name(), "{}", "parameter 'g' is nullptr");
        return std::set<std::set<std::shared_ptr<gate>>>();
    }
    /* check validity of gates */
    if (gates.empty())
    {
        gates = g->get_gates();
    }
    for (const auto& current_gate : gates)
    {
        if (current_gate != nullptr)
            continue;
        log_error(this->get_name(), "{}", "parameter 'gates' contains a nullptr");
        return std::set<std::set<std::shared_ptr<gate>>>();
    }

    boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> boost_graph;
    typedef boost::graph_traits<boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS>>::vertex_descriptor vertex_t;

    /* add ordered gates to directed boost graph */
    std::set<u32> gate_ids;
    for (const auto& current_gate : gates)
        gate_ids.insert(current_gate->get_id());

    std::map<u32, vertex_t> gate_id_to_vertex;
    std::map<vertex_t, u32> vertex_id_to_gate_id;
    u32 vertex_id_cnt = 0;
    for (const auto& gate_id : gate_ids)
    {
        auto vertex_descriptor                = boost::add_vertex(boost_graph);
        gate_id_to_vertex[gate_id]            = vertex_descriptor;
        vertex_id_to_gate_id[vertex_id_cnt++] = gate_id;
    }

    /* add ordered edges to directed boost graph */
    std::map<u32, std::shared_ptr<net>> ordered_nets;
    for (const auto& current_gate : gates)
    {
        for (const auto& net : current_gate->get_fan_out_nets())
            ordered_nets[net->get_id()] = net;
    }

    for (auto it_net : ordered_nets)
    {
        auto src = it_net.second->get_src();
        if (src.gate == nullptr)
            continue;

        std::set<u32> dst_ids;
        for (auto dst : it_net.second->get_dsts())
            dst_ids.insert(dst.gate->get_id());

        for (const auto& dst_id : dst_ids)
            boost::add_edge(gate_id_to_vertex[src.gate->get_id()], gate_id_to_vertex[dst_id], boost_graph);
    }

    /* initialize parameters for strong_components() */
    std::vector<int> component(g->get_gates().size());
    std::vector<int> discover_time(g->get_gates().size());
    std::vector<boost::default_color_type> color(g->get_gates().size());
    std::vector<vertex_t> root(g->get_gates().size());
    int num = strong_components(boost_graph,
                                make_iterator_property_map(component.begin(), boost::get(boost::vertex_index, boost_graph)),
                                root_map(make_iterator_property_map(root.begin(), boost::get(boost::vertex_index, boost_graph)))
                                    .color_map(make_iterator_property_map(color.begin(), boost::get(boost::vertex_index, boost_graph)))
                                    .discover_time_map(make_iterator_property_map(discover_time.begin(), boost::get(boost::vertex_index, boost_graph))));

    log("SCC BOOST: {}", num);

    /* post process boost result */
    std::map<int, std::set<u32>> component_to_gate_ids;
    for (std::vector<int>::size_type i = 0; i != component.size(); ++i)
        component_to_gate_ids[component[i]].insert(vertex_id_to_gate_id[i]);

    std::set<std::set<std::shared_ptr<gate>>> result;
    for (auto it_component : component_to_gate_ids)
    {
        std::set<std::shared_ptr<gate>> component_set;
        for (const auto& gate_id : it_component.second)
            component_set.insert(g->get_gate_by_id(gate_id));
        if (!component_set.empty())
            result.insert(component_set);
    }
    log_debug(this->get_name(), "found {} components in graph ", num);
    return result;
}


//  boost/chrono/stopwatches/collectors/laps_accumulator_set.hpp
//  Copyright 2011 Vicente J. Botet Escriba
//  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)
//  See http://www.boost.org/libs/chrono/stopwatches for documentation.

#ifndef BOOST_CHRONO_STOPWATCHES_MEMORIES_LAPS_ACCUMULATOR_SET_HPP
#define BOOST_CHRONO_STOPWATCHES_MEMORIES_LAPS_ACCUMULATOR_SET_HPP

#include <boost/chrono/stopwatches/collectors/last_lap.hpp>
#include <boost/accumulators/framework/accumulator_set.hpp>
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/sum.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/framework/features.hpp>

namespace boost
{
  namespace chrono
  {

    template<
      typename Duration,
      typename Features = accumulators::features<accumulators::tag::count,
        accumulators::tag::sum, accumulators::tag::min,
        accumulators::tag::max, accumulators::tag::mean>,
      typename Weight = void>
    struct laps_accumulator_set : last_lap<Duration>
    {
      typedef last_lap<Duration> base_type;
      typedef Duration duration;
      typedef typename duration::rep rep;
      typedef accumulators::accumulator_set<rep, Features,
          Weight> storage_type;
      storage_type acc_;

      void store(duration const& d)
      {
        this->base_type::store(d);
        acc_(d.count());
      }

      void reset()
      {
        this->base_type::reset();
        acc_ = storage_type();
      }

      storage_type const& accumulator_set() const  { return acc_; }

      duration elapsed() const  { return duration(accumulators::sum(acc_)); }

    };

  } // namespace chrono
} // namespace boost

#endif


/*
 * Copyright (c) 2009 Carnegie Mellon University.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 * For more about this software visit:
 *
 *      http://www.graphlab.ml.cmu.edu
 *
 */

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <boost/unordered_map.hpp>
#include <time.h>

#include <graphlab.hpp>
#include <graphlab/graph/distributed_graph.hpp>

struct vdata {
  std::vector<size_t> vids;
  void save(graphlab::oarchive& oarc) const {
    oarc << vids;
  }
  void load(graphlab::iarchive& iarc) {
    iarc >> vids;
  }
};

typedef graphlab::distributed_graph<vdata, graphlab::empty> graph_type;

void vertex_combine(vdata& a, const vdata& b) {
  for (size_t i = 0;i < b.vids.size(); ++i) a.vids.push_back(b.vids[i]);
}

bool ccoutput_parser(graph_type& graph, const std::string& filename, const std::string& textline) {
  size_t split = textline.find_first_of(",");
  if (split == std::string::npos) return true;
  else {
    std::string t = textline;
    t[split] = 0;
    vdata data;
    data.vids.push_back(atol(t.c_str()));
    graph.add_vertex(atol(t.c_str() + split + 1), data);
    return true;
  }
}

struct size_counter {
  // a map from size to count
  boost::unordered_map<size_t, size_t> counts;

  size_counter() { }

  explicit size_counter(size_t size) {
    counts[size] = 1;
  }

  size_counter& operator+=(const size_counter& other) {
    boost::unordered_map<size_t, size_t>::const_iterator iter = other.counts.begin();
    while(iter != other.counts.end()) {
      counts[iter->first] += iter->second;
      ++iter;
    }
    return *this;
  }

  void save(graphlab::oarchive& oarc) const {
    oarc << counts;
  }
  void load(graphlab::iarchive& iarc) {
    iarc >> counts;
  }
};

size_counter absolute_vertex_data(const graph_type::vertex_type& vertex) {
  return size_counter(vertex.data().vids.size());
}

class graph_writer {
public:
  std::string save_vertex(graph_type::vertex_type v) {
    std::stringstream strm;
    strm << v.id() << ":";
    for (size_t i = 0;i < v.data().vids.size(); ++i) {
      strm << v.data().vids[i] << " ";
    }
    strm << "\n";
    return strm.str();
  }
  std::string save_edge(graph_type::edge_type e) {
    return "";
  }
};

int main(int argc, char** argv) {
  std::cout << "Connected Component\n\n";

  graphlab::mpi_tools::init(argc, argv);
  graphlab::distributed_control dc;
  //parse options
  graphlab::command_line_options clopts("Connected Component Stats.");
  std::string graph_dir;
  std::string saveprefix;
  std::string format = "adj";
  clopts.attach_option("graph", graph_dir,
                       "The graph file. This is not optional");
  clopts.add_positional("graph");
  clopts.attach_option("format", format,
                       "The graph file format");
  clopts.attach_option("saveprefix", saveprefix,
                       "save location");
  if (!clopts.parse(argc, argv)) {
    dc.cout() << "Error in parsing command line arguments." << std::endl;
    return EXIT_FAILURE;
  }
  if (graph_dir == "") {
    std::cout << "--graph is not optional\n";
    return EXIT_FAILURE;
  }

  graph_type graph(dc, clopts);

  //load graph
  dc.cout() << "Loading graph in format: "<< format << std::endl;
  graphlab::timer ti;
  graph.set_duplicate_vertex_strategy(vertex_combine);
  graph.load(graph_dir, ccoutput_parser);
  graph.finalize();
  dc.cout() << "Complete Finalization in " << ti.current_time() << std::endl;

  ti.start();
  //take statistics
  size_counter stat = graph.map_reduce_vertices<size_counter>(
      absolute_vertex_data);

  dc.cout() << "graph calculation time is " << ti.current_time() << " sec\n";
  dc.cout() << "RESULT:\nsize\tcount\n";
  for (boost::unordered_map<size_t, size_t>::const_iterator iter = stat.counts.begin();
      iter != stat.counts.end(); iter++) {
    dc.cout() << iter->first << "\t" << iter->second << "\n";
  }

  //write results
  if (saveprefix.size() > 0) {
    graph.save(saveprefix, graph_writer(),
        false, //set to true if each output file is to be gzipped
        true, //whether vertices are saved
        false); //whether edges are saved
  }

  graphlab::mpi_tools::finalize();

  return EXIT_SUCCESS;
}


// The gene graph is a graph in which each vertex
// corresponds to a gene.
// An undirected edge is created between two vertices
// if the there is good similarity between the
// expression vectors of the corresponding genes.

#ifndef CZI_EXPRESSION_MATRIX2_GENE_GRAPH_HPP
#define CZI_EXPRESSION_MATRIX2_GENE_GRAPH_HPP

#include "Ids.hpp"

#include <boost/graph/adjacency_list.hpp>

#include "array.hpp"
#include "iosfwd.hpp"
#include "map.hpp"
#include "string.hpp"

namespace ChanZuckerberg {
    namespace ExpressionMatrix2 {

        class GeneGraph;
        class GeneGraphVertex;
        class GeneGraphEdge;
        class ExpressionMatrix;
        class GeneSet;

        // The base class for class CellGraph.
        using GeneGraphBaseClass = boost::adjacency_list<
            boost::setS,    // Prevent parallel edges.
            boost::listS,
            boost::undirectedS,
            GeneGraphVertex,
            GeneGraphEdge>;

    }
}

// A vertex of the gene graph.
class ChanZuckerberg::ExpressionMatrix2::GeneGraphVertex {
public:
    GeneId localGeneId = invalidGeneId;     // Gene id as defined by the GeneSet.
    GeneId globalGeneId = invalidGeneId;    // Gene id as defined by the ExpressionMatrix.
    array<double, 2> position;
    string color;
    GeneGraphVertex() {}
    GeneGraphVertex(GeneId localGeneId, GeneId globalGeneId) :
        localGeneId(localGeneId), globalGeneId(globalGeneId) {}
};

// An edge of the cell graph.
class ChanZuckerberg::ExpressionMatrix2::GeneGraphEdge {
public:
    float similarity = -1.;

    GeneGraphEdge()
    {
    }

    GeneGraphEdge(float similarity) :
        similarity(similarity)
    {
    }
};

class ChanZuckerberg::ExpressionMatrix2::GeneGraph : public GeneGraphBaseClass {
public:

    // Use the constructors of the base class.
    using GeneGraphBaseClass::GeneGraphBaseClass;

    using Graph = GeneGraph ;
    Graph& graph()
    {
        return *this;
    }
    const Graph& graph() const
    {
        return *this;
    }

    GeneGraph(
        ostream& out,
        GeneSet&,
        const string& similarGenePairsName,
        double similarityThreshold,
        size_t maxConnectivity
        );

    // Write out the signature graph in Graphviz format.
    void writeGraphviz(const string& fileName) const;
    void writeGraphviz(ostream&) const;

    // Parameters to control svg output.
    class SvgParameters {
    public:
        // Parameters that can be set arbitrarily.
        bool hideEdges = false;
        double svgSizePixels = 500;
        double xShift = 0.;
        double yShift = 0.;
        double zoomFactor = 1.;
        double vertexSizeFactor = 1.;
        double edgeThicknessFactor = 1.;
        // Parameters derived from the above values.
        // These are filled in by writeSvg.
        double xCenter;
        double yCenter;
        double halfViewBoxSize;
        double pixelSize;
    };

    // Use Graphviz to compute the graph layout and store it in the vertex positions.
    void computeLayout();

    // Write out the gene graph in SVG format.
    void writeSvg(
        const string& fileName,
        SvgParameters&,
        const ExpressionMatrix&);
    void writeSvg(
        ostream& s,
        SvgParameters&,
        const ExpressionMatrix&);

    // Map from local gene id to vertex descriptor.
    map<GeneId, vertex_descriptor> vertexTable;

private:
    class Writer {
    public:
        Writer(const GeneGraph&);
        void operator()(ostream&) const;
        void operator()(ostream&, vertex_descriptor) const;
        void operator()(ostream&, edge_descriptor) const;
        const GeneGraph& graph;
    };

    bool layoutWasComputed = false;
};

#endif


// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Use, modification and distribution is subject to 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)
//
// Projection example 3, combined with shapelib and SVG

#include <fstream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>

#include <boost/geometry/io/svg/write_svg.hpp>
#include <boost/geometry/extensions/gis/latlong/latlong.hpp>
#include <boost/geometry/extensions/gis/projections/project_transformer.hpp>

void read_wkt_and_project_and_write_svg(std::string const& wkt_filename,
        std::string const& projection_parameters,
        std::string const& svg_filename)
{
    using namespace boost::geometry;

    // Declare a vector containing the world countries
    typedef model::ll::point<degree> point_ll_deg;
    typedef model::polygon<point_ll_deg> polygon_ll_deg;
    std::vector<polygon_ll_deg> ll_polygons;

    typedef model::d2::point_xy<double> point_xy;

    // Read polygons from a Well-Known Text file using the ggl parser
    std::ifstream cpp_file(wkt_filename.c_str());
    if (! cpp_file.is_open())
    {
        throw std::string("File not found: ") + wkt_filename;
    }

    while (! cpp_file.eof() )
    {
        std::string line;
        std::getline(cpp_file, line);
        if (boost::starts_with(line, "MULTIPOLYGON"))
        {
            typedef boost::geometry::model::multi_polygon<boost::geometry::model::polygon<point_ll_deg> > mp_type;
            mp_type mp;
            boost::geometry::read_wkt(line, mp);
            for (mp_type::const_iterator it = boost::begin(mp);
                it != boost::end(mp); ++it)
            {
                ll_polygons.push_back(*it);
            }
        }
    }

    // Our latlong polygon collection will be projected into this vector
    // (Of course it is also possible to do this while reading and have one vector)
    std::vector<model::polygon<point_xy> > xy_polygons;

    // Declare transformation strategy which contains a projection
    projections::project_transformer
        <
            point_ll_deg,
            point_xy
        > projection(projection_parameters);

    // Project the polygons, and at the same time get the bounding box (in xy)
    model::box<point_xy> bbox;
    assign_inverse(bbox);
    for (std::vector<polygon_ll_deg>::const_iterator it = ll_polygons.begin();
         it != ll_polygons.end();
         ++it)
    {
        model::polygon<point_xy> xy_polygon;

        if (transform(*it, xy_polygon, projection))
        {
            // Update bbox with box of this projected polygon
            expand(bbox, return_envelope<model::box<point_xy> >(xy_polygon));

            // Add projected polygon
            xy_polygons.push_back(xy_polygon);
        }
    }

    // Create an SVG image
    std::ofstream out(svg_filename.c_str());
    out
        << "<?xml version=\"1.0\" standalone=\"no\"?>" << std::endl
        << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"" << std::endl
        << "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << std::endl
        << "<svg width=\"100%\" height=\"100%\" version=\"1.1\"" << std::endl
        << "xmlns=\"http://www.w3.org/2000/svg\">" << std::endl;

    // Setup the transformation to SVG
    // (alternatively this could be skipped because SVG can transform itself,
    // but this example shows it like this)
    typedef boost::geometry::model::d2::point_xy<int> svg_point;
    boost::geometry::strategy::transform::map_transformer
        <
            double, 2, 2,
            true, true
        > svg_transformer(bbox, 800, 600);

    // Create the background
    boost::geometry::model::box<svg_point> box;
    boost::geometry::assign_values(box, 0, 0, 800, 600);
    out << boost::geometry::svg(box, "fill:rgb(0,0,255)") << std::endl;

    for (std::vector<model::polygon<point_xy> >::const_iterator it = xy_polygons.begin();
         it != xy_polygons.end();
         ++it)
    {
        boost::geometry::model::polygon<svg_point> svg_polygon;
        boost::geometry::transform(*it, svg_polygon, svg_transformer);
        out << boost::geometry::svg(svg_polygon,
            "fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:0.2")
            << std::endl;
    }
    out << "</svg>" << std::endl;
}

int main(int argc, char** argv)
{
    try
    {
        // Note, file location: trunk/libs/geometry/example/data
        // update path below if necessary
        read_wkt_and_project_and_write_svg(
            "../../../../example/data/world.wkt",
            "+proj=moll +ellps=clrk66",
            "world.svg");
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
        return 1;
    }
    catch(std::string const& s)
    {
        std::cout << "Exception: " << s << std::endl;
        return 1;
    }

    return 0;
}


/**
 * Copyright (c) 2009 Carnegie Mellon University.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 * For more about this software visit:
 *
 *      http://www.graphlab.ml.cmu.edu
 *
 */

/**
 * \file
 *
 * \brief The main file for the NMF matrix factorization algorithm.
 *
 */

#include <graphlab/util/stl_util.hpp>
#include <graphlab.hpp>
#include <graphlab/engine/gl3engine.hpp>
#include <Eigen/Dense>
#include "eigen_serialization.hpp"
#include <graphlab/macros_def.hpp>

#define VERTEX_DELTA_TASK_ID 0

typedef Eigen::VectorXd vec_type;
typedef Eigen::MatrixXd mat_type;

//when using negative node id range, we are not allowed to use
//0 and 1 so we add 2.
const static int SAFE_NEG_OFFSET=2;
static bool debug;
int iter = 0;
enum { PHASE1, PHASE2};
int phase = PHASE1;
double epsilon = 1e-16;

bool isuser(uint node){
  return ((int)node) >= 0;
}

/**
 * \ingroup toolkit_matrix_pvecization
 *
 * \brief the vertex data type which contains the latent pvec.
 *
 * Each row and each column in the matrix corresponds to a different
 * vertex in the SGD graph.  Associated with each vertex is a pvec
 * (vector) of latent parameters that represent that vertex.  The goal
 * of the SGD algorithm is to find the values for these latent
 * parameters such that the non-zero entries in the matrix can be
 * predicted by taking the dot product of the row and column pvecs.
 */
struct vertex_data {
  /**
   * \brief A shared "constant" that specifies the number of latent
   * values to use.
   */
  static size_t NLATENT;
  /** \brief The latent pvec for this vertex */
  vec_type pvec;

  int nupdates;

  /**
   * \brief Simple default constructor which randomizes the vertex
   *  data
   */
  vertex_data() { if (debug) pvec = vec_type::Ones(NLATENT); else randomize(); }
  /** \brief Randomizes the latent pvec */
  void randomize() { pvec.resize(NLATENT); pvec.setRandom(); }
  /** \brief Save the vertex data to a binary archive */
  void save(graphlab::oarchive& arc) const {
    arc << pvec;
  }
  /** \brief Load the vertex data from a binary archive */
  void load(graphlab::iarchive& arc) {
    arc >> pvec;
  }
}; // end of vertex data

std::size_t hash_value(vertex_data const& b) {
  return b.nupdates;
}

/**
 * \brief The edge data stores the entry in the matrix.
 *
 * In addition the edge data sgdo stores the most recent error estimate.
 */
struct edge_data : public graphlab::IS_POD_TYPE {
  /**
   * \brief The type of data on the edge;
   *
   * \li *Train:* the observed value is correct and used in training
   * \li *Validate:* the observed value is correct but not used in training
   * \li *Predict:* The observed value is not correct and should not be
   *        used in training.
   */
  enum data_role_type { TRAIN, VALIDATE, PREDICT  };

  /** \brief the observed value for the edge */
  float obs;

  /** \brief The train/validation/test designation of the edge */
  data_role_type role;

  /** \brief basic initialization */
  edge_data(float obs = 0, data_role_type role = PREDICT) :
    obs(obs), role(role) { }

}; // end of edge data

std::size_t hash_value(edge_data const& b) {
  return boost::hash_value(b.obs);
}

/**
 * \brief The graph type is defined in terms of the vertex and edge
 * data.
 */
typedef graphlab::distributed_graph<vertex_data, edge_data> graph_type;
typedef graphlab::gl3engine<graph_type> engine_type;

vec_type x1;
vec_type x2;
vec_type * px;

bool isuser_node(const graph_type::vertex_type& vertex){
  return isuser(vertex.id());
}

/**
 * \brief The graph loader function is a line parser used for
 * distributed graph construction.
 */
inline bool graph_loader(graph_type& graph,
                         const std::string& filename,
                         const std::string& line) {
  ASSERT_FALSE(line.empty());
  // Determine the role of the data
  edge_data::data_role_type role = edge_data::TRAIN;
  if(boost::ends_with(filename,".validate")) role = edge_data::VALIDATE;
  else if(boost::ends_with(filename, ".predict")) role = edge_data::PREDICT;
  // Parse the line
  std::stringstream strm(line);
  graph_type::vertex_id_type source_id(-1), target_id(-1);
  float obs(0);
  strm >> source_id >> target_id;

  // for test files (.predict) no need to read the actual rating value.
  if(role == edge_data::TRAIN || role == edge_data::VALIDATE) {
    strm >> obs;
  }
  target_id = -(graphlab::vertex_id_type(target_id + SAFE_NEG_OFFSET));

  // Create an edge and add it to the graph
  graph.add_edge(source_id, target_id, edge_data(obs, role));
  return true; // successful load
} // end of graph_loader

void vertex_delta(graph_type::vertex_type& vtx, const vec_type& delta) {
  if (delta.sum() != 0)
    vtx.data().pvec.array() *= delta.array() / px->array();
  for (uint i=0; i< vertex_data::NLATENT; i++)
    if (vtx.data().pvec[i] < epsilon)
      vtx.data().pvec[i] = epsilon;
}

void nmf_function(engine_type::context_type& context,
                  graph_type::edge_type& edge) {
  double pred = edge.source().data().pvec.dot(edge.target().data().pvec);
  if (pred == 0)
     logstream(LOG_FATAL)<<"Got into numerical error!" << std::endl;
  vec_type delta;
  delta = (phase == PHASE1 ? edge.target().data().pvec : edge.source().data().pvec) * edge.data().obs / pred;
  context.send_delta(VERTEX_DELTA_TASK_ID, phase == PHASE1 ? edge.source() : edge.target(), delta);
}

vec_type count_edges(const graph_type::edge_type& edge) {
  vec_type ret = vec_type::Zero(2);
  if (edge.data().role == edge_data::TRAIN){
    ret[0] = 1;
  }
  else if (edge.data().role == edge_data::VALIDATE){
    ret[1] = 1;
  }
  if (edge.data().obs < 0)
    logstream(LOG_FATAL)<<"Found a negative entry in matirx row " << edge.source().id() << " with value: " << edge.data().obs << std::endl;
  return ret;
}

void verify_rows(
    graph_type::vertex_type& vertex){
        if (isuser(vertex.id()) && vertex.num_out_edges() == 0)
          logstream(LOG_FATAL)<<"NMF algorithm can not work when the row " << vertex.id() << " of the matrix contains all zeros" << std::endl;
}

vec_type pre_iter( const graph_type::vertex_type & vertex){
  return vertex.data().pvec;
}

void sync_function(engine_type::context_type& context,
                   graph_type::vertex_type& vertex) {
  context.synchronize(vertex);
}

/**
 * \brief Given an edge compute the error associated with that edge
 */
double extract_l2_error(const graph_type::edge_type & edge) {
  double pred =
      edge.source().data().pvec.dot(edge.target().data().pvec);
  double rmse = (edge.data().obs - pred) * (edge.data().obs - pred);
  return rmse;
} // end of extract_l2_error

size_t vertex_data::NLATENT = 20;

int main(int argc, char** argv) {
  global_logger().set_log_level(LOG_INFO);
  global_logger().set_log_to_console(true);

  // Parse command line options -----------------------------------------------
  const std::string description =
      "Compute the ALS factorization of a matrix.";
  graphlab::command_line_options clopts(description);
  std::string input_dir;
  size_t interval = 0;
  size_t ITERATIONS = 10;
  std::string exec_type = "synchronous";
  clopts.attach_option("matrix", input_dir,
                       "The directory containing the matrix file");
  clopts.add_positional("matrix");
  clopts.attach_option("D", vertex_data::NLATENT,
                       "Number of latent parameters to use.");
  clopts.attach_option("interval", interval,
                       "The time in seconds between error reports");
  clopts.attach_option("iterations", ITERATIONS,
                       "number of NMF iterations");
  if(!clopts.parse(argc, argv) || input_dir == "") {
    std::cout << "Error in parsing command line arguments." << std::endl;
    clopts.print_description();
    return EXIT_FAILURE;
  }
  ///! Initialize control plain using mpi
  graphlab::mpi_tools::init(argc, argv);
  graphlab::distributed_control dc;

  dc.cout() << "Loading graph." << std::endl;
  graphlab::timer timer;
  graph_type graph(dc, clopts);
  graph.load(input_dir, graph_loader);
  dc.cout() << "Loading graph. Finished in "
            << timer.current_time() << std::endl;

  dc.cout() << "Finalizing graph." << std::endl;
  timer.start();
  graph.finalize();
  dc.cout() << "Finalizing graph. Finished in "
            << timer.current_time() << std::endl;

  if (!graph.num_edges() || !graph.num_vertices())
     logstream(LOG_FATAL)<< "Failed to load graph. Check your input path: " << input_dir << std::endl;

  dc.cout()
      << "========== Graph statistics on proc " << dc.procid()
      << " ==============="
      << "\n Num vertices: " << graph.num_vertices()
      << "\n Num edges: " << graph.num_edges()
      << "\n Num replica: " << graph.num_replicas()
      << "\n Replica to vertex ratio: "
      << float(graph.num_replicas())/graph.num_vertices()
      << "\n --------------------------------------------"
      << "\n Num local own vertices: " << graph.num_local_own_vertices()
      << "\n Num local vertices: " << graph.num_local_vertices()
      << "\n Replica to own ratio: "
      << (float)graph.num_local_vertices()/graph.num_local_own_vertices()
      << "\n Num local edges: " << graph.num_local_edges()
      //<< "\n Begin edge id: " << graph.global_eid(0)
        << "\n Edge balance ratio: "
        << float(graph.num_local_edges())/graph.num_edges()
        << std::endl;

  dc.cout() << "Creating engine" << std::endl;

  engine_type engine(dc, graph, clopts);
  vec_type edge_count = graph.map_reduce_edges<vec_type>(count_edges);
  dc.cout()<<"Training edges: " << edge_count[0] << " validation edges: " << edge_count[1] << std::endl;

  graphlab::vertex_set left = graph.select(isuser_node);
  graphlab::vertex_set right = ~left;
  graph.transform_vertices(verify_rows, left);

  engine.register_vertex_delta<vec_type>(VERTEX_DELTA_TASK_ID, vertex_delta);

  dc.cout() << "Running NMF" << std::endl;

  timer.start();
  for (size_t i = 0;i < ITERATIONS; ++i) {
    phase = PHASE1;
    x1 = graph.map_reduce_vertices<vec_type>(pre_iter,right);
    px = &x1;
    dc.cout() <<"x1 is: " << x1 << std::endl;

    engine.parfor_all_local_edges(nmf_function); //todo - only left
    engine.parfor_all_local_vertices(sync_function); //todo - only left
    engine.wait();

    phase = PHASE2;

    x2 = graph.map_reduce_vertices<vec_type>(pre_iter,left);
    px = &x2;
    dc.cout() <<"x2 is: " << x2 << std::endl;

    engine.parfor_all_local_edges(nmf_function); //todo only right
    engine.parfor_all_local_vertices(sync_function); //todo only right
    engine.wait();

    double rmse = graph.map_reduce_edges<double>(extract_l2_error);
    dc.cout() << "RMSE = " << sqrt(rmse / graph.num_edges()) << std::endl;

  }

  const double runtime = timer.current_time();
  dc.cout() << "----------------------------------------------------------"
            << std::endl
            << "Final Runtime (seconds):   " << runtime;

  // Compute the final training error -----------------------------------------
  dc.cout() << "Final error: " << std::endl;

  graphlab::mpi_tools::finalize();
  return EXIT_SUCCESS;
} // end of main


/*===================================================================

MSI applications for interactive analysis in MITK (M2aia)

Copyright (c) Jonas Cordes.

All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or https://www.github.com/jtfcordes/m2aia for details.

===================================================================*/

#include <algorithm>
#include <boost/progress.hpp>
#include <itkImage.h>
#include <itksys/SystemTools.hxx>
#include <m2ImzMLSpectrumImage.h>
#include <m2Process.hpp>
#include <mitkCommandLineParser.h>
#include <mitkIOUtil.h>
#include <mitkImage.h>
#include <mutex>
#include <numeric>
#include <signal/m2PeakDetection.h>
#include <stdlib.h>

std::map<std::string, us::Any> CommandlineParsing(int argc, char *argv[]);

int main(int argc, char *argv[])
{

  std::map<std::string, us::Any> argsMap;
  std::string params = "";

  if(argc > 1){
    argsMap = CommandlineParsing(argc, argv);
    auto ifs = std::ifstream(argsMap["parameterfile"].ToString());
    params = std::string(std::istreambuf_iterator<char>{ifs}, {});
  }

  using namespace std::string_literals;
  std::map<std::string, std::string> pMap;
  const auto bsc_s = m2::Find(params, "baseline-correction", "None"s, pMap);
  const auto bsc_hw = m2::Find(params, "baseline-correction-hw", int(50), pMap);
  const auto sm_s = m2::Find(params, "smoothing", "None"s, pMap);
  const auto sm_hw = m2::Find(params, "smoothing-hw", int(2), pMap);
  const auto norm = m2::Find(params, "normalization", "None"s, pMap);
  const auto pool = m2::Find(params, "pooling", "Maximum"s, pMap);
  const auto tol = m2::Find(params, "tolerance", double(0), pMap);
  const auto threads = m2::Find(params, "threads", int(itk::MultiThreader::GetGlobalDefaultNumberOfThreads()), pMap);
  const auto binning_tol = m2::Find(params, "binning-tolerance", double(0), pMap);
  const auto SNR = m2::Find(params, "SNR", double(1.5), pMap);
  const auto peakpicking_hw = m2::Find(params, "peakpicking-hw", int(5), pMap);
  const auto monoisotopick = m2::Find(params, "monoisotopic", bool(false), pMap);
  const auto y_output_type = m2::Find(params, "y-type", "Float"s, pMap);
  const auto x_output_type = m2::Find(params, "x-type", "Float"s, pMap);

  if (argsMap.find("parameterfile") == argsMap.end())
  {
    try
    {
      using namespace itksys;
      auto cwd = SystemTools::GetCurrentWorkingDirectory();
      auto path = SystemTools::ConvertToOutputPath(SystemTools::JoinPath({cwd, "/m2PeakPicking.txt.sample"}));
      std::ofstream ofs(path);
      for(auto kv : pMap){
        ofs << "(" << kv.first  << " " << kv.second << ")\n";
      }
      MITK_INFO << "A dummy parameter file was written to " << path;
      return 0;
    }
    catch (std::exception &e)
    {
      MITK_INFO << "Error on writing a dummy parameter file! " << e.what();
      return 2;
    }

  }

  auto image = mitk::IOUtil::Load(argsMap["input"].ToString()).front();

  for (auto kv : argsMap)
  {
    MITK_INFO << kv.first << " " << kv.second.ToString();
  }
  if (auto sImage = dynamic_cast<m2::SpectrumImageBase *>(image.GetPointer()))
  {
    if (sImage->GetSpectrumType().Format != m2::SpectrumFormat::ContinuousProfile)
    {
      MITK_ERROR << "Only imzML files in continuous profile mode are accepted for peak picking!";
      return 1;
    }

    sImage->SetBaselineCorrectionStrategy(static_cast<m2::BaselineCorrectionType>(m2::BASECOR_MAPPINGS.at(bsc_s)));
    sImage->SetBaseLineCorrectionHalfWindowSize(bsc_hw);

    sImage->SetSmoothingStrategy(static_cast<m2::SmoothingType>(m2::SMOOTHING_MAPPINGS.at(sm_s)));
    sImage->SetSmoothingHalfWindowSize(sm_hw);

    sImage->SetNormalizationStrategy(static_cast<m2::NormalizationStrategyType>(m2::NORMALIZATION_MAPPINGS.at(norm)));
    sImage->SetRangePoolingStrategy(static_cast<m2::RangePoolingStrategyType>(m2::POOLING_MAPPINGS.at(pool)));

    sImage->InitializeImageAccess();
    sImage->SetTolerance(tol);
    sImage->SetBinningTolerance(binning_tol);

    sImage->GetExportSpectrumType().Format = (m2::SpectrumFormat::ProcessedCentroid);
    sImage->GetExportSpectrumType().YAxisType = (static_cast<m2::NumericType>(m2::CORE_MAPPINGS.at(y_output_type)));
    sImage->GetExportSpectrumType().XAxisType = (static_cast<m2::NumericType>(m2::CORE_MAPPINGS.at(x_output_type)));

    if (auto imzMLImage = dynamic_cast<m2::ImzMLSpectrumImage *>(sImage))
    {
      const auto &xValues = imzMLImage->GetXAxis();
      int sourceId = 0;

      for (auto &source : imzMLImage->GetImzMLSpectrumImageSourceList())
      {
        MITK_INFO << "Start peak picking for " << source.m_Spectra.size() << " spectra...";
        boost::progress_display show_progress(source.m_Spectra.size());

        m2::Process::Map(source.m_Spectra.size(),
                         threads,
                         [&](const auto /*tId*/, const auto s, const auto e)
                         {
                           std::vector<float> yValues(xValues.size());
                           std::vector<float> xValues(xValues.size());
                           for (unsigned int spectrumId = s; spectrumId < e; ++spectrumId)
                           {
                             imzMLImage->GetSpectrum(spectrumId, xValues, yValues, sourceId);
                             source.m_Spectra[spectrumId].peaks =
                               m2::Signal::PickPeaks(xValues, yValues, SNR, peakpicking_hw, binning_tol, monoisotopick);
                             ++show_progress;
                           }
                         });
        ++sourceId;

        float sumPeaks = 0;
        for (const auto &p : source.m_Spectra)
          sumPeaks += p.peaks.size();

        MITK_INFO << "Average number of peaks " << sumPeaks / double(source.m_Spectra.size());
      }
    }

    mitk::IOUtil::Save(sImage, argsMap["output"].ToString());
  }
}

std::map<std::string, us::Any> CommandlineParsing(int argc, char *argv[])
{
  mitkCommandLineParser parser;
  parser.setArgumentPrefix("--", "-");
  // required params
  parser.addArgument("input",
                     "i",
                     mitkCommandLineParser::Image,
                     "Input imzML Image",
                     "Path to the input imzML",
                     us::Any(),
                     false,
                     false,
                     false,
                     mitkCommandLineParser::Input);
  parser.addArgument("parameterfile",
                     "p",
                     mitkCommandLineParser::File,
                     "Parameter file",
                     "A dummy parameter file can be generated by calling the app without any arguments.",
                     us::Any(),
                     false,
                     false,
                     false,
                     mitkCommandLineParser::Input);
  parser.addArgument("output",
                     "o",
                     mitkCommandLineParser::Image,
                     "Output Image",
                     "Path to the output image path",
                     us::Any(),
                     false,
                     false,
                     false,
                     mitkCommandLineParser::Output);

  // Miniapp Infos
  parser.setCategory("M2aia Tools");
  parser.setTitle("Pixel-wise peak picking");
  parser.setDescription(
    "Reads an imzML file and start a pixel-wise peak picking. https://m2aia.de (https://bio.tools/m2aia)");
  parser.setContributor("Jonas Cordes");

  auto parsedArgs = parser.parseArguments(argc, argv);

  if (parsedArgs.size() == 0)
  {
    exit(EXIT_SUCCESS);
  }

  return parsedArgs;
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef NT2_STATISTICS_FUNCTIONS_CAURND_HPP_INCLUDED
#define NT2_STATISTICS_FUNCTIONS_CAURND_HPP_INCLUDED
#include <nt2/include/functor.hpp>
#include <nt2/sdk/meta/size_as.hpp>
#include <nt2/sdk/meta/value_as.hpp>
#include <nt2/core/container/dsl/size.hpp>
#include <nt2/core/container/dsl/value_type.hpp>

#include <nt2/sdk/parameters.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>

/*!
 * \ingroup statistics
 * \defgroup statistics_cau caurnd
 *
 * \par Description
 * cauchy pseudo random numbers generator : inverse cumulative method using rand
 * All rnd (cumulative distribution functions  can be called with the syntax
 * r = xxxrnd(param_1, ...,  param_n,  sizing_params)
 * the type of params elements determines the type of the output expression elements.
 * cauchy has 2 parameters: median and scale
 * parameters elements have no defaults
 * \par
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/caurnd.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace nt2
 * {
 *   template <class A0>
 *     meta::call<tag::caurnd_(A0)>::type
 *     caurnd(const A0 & m, const A1 & l, ... dimensions);
 * }
 * \endcode
 *
**/

namespace nt2 { namespace tag
  {
    /*!
     * \brief Define the tag caurnd_ of functor caurnd
     *        in namespace nt2::tag for toolbox statistics
    **/
    struct caurnd_ : ext::unspecified_<caurnd_> { typedef ext::unspecified_<caurnd_> parent; };
  }

 NT2_FUNCTION_IMPLEMENTATION(nt2::tag::caurnd_, caurnd, 2)
 NT2_FUNCTION_IMPLEMENTATION(nt2::tag::caurnd_, caurnd, 3)
 NT2_FUNCTION_IMPLEMENTATION(nt2::tag::caurnd_, caurnd, 4)
 NT2_FUNCTION_IMPLEMENTATION(nt2::tag::caurnd_, caurnd, 5)
 NT2_FUNCTION_IMPLEMENTATION(nt2::tag::caurnd_, caurnd, 6)
}

namespace nt2 { namespace ext
{
  template<class Domain, class Expr, int N>
  struct  value_type<tag::caurnd_,Domain,N,Expr>
        : meta::value_as<Expr,0>
  {};

  template<class Domain, class Expr, int N>
  struct  size_of<tag::caurnd_,Domain,N,Expr>
        : meta::boxed_size<Expr,2>
  {};
} }

#endif


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 * @file VectorValues.cpp
 * @brief Implementations for VectorValues
 * @author Richard Roberts
 * @author Alex Cunningham
 */

#include <gtsam/base/FastVector.h>
#include <gtsam/inference/Permutation.h>
#include <gtsam/linear/VectorValues.h>

#include <boost/iterator/counting_iterator.hpp>

using namespace std;

namespace gtsam {

/* ************************************************************************* */
VectorValues VectorValues::Zero(const VectorValues& x) {
  VectorValues result;
  result.values_.resize(x.size());
  for(size_t j=0; j<x.size(); ++j)
    result.values_[j] = Vector::Zero(x.values_[j].size());
  return result;
}

/* ************************************************************************* */
vector<size_t> VectorValues::dims() const {
  std::vector<size_t> result(this->size());
  for(Index j = 0; j < this->size(); ++j)
    result[j] = this->dim(j);
  return result;
}

/* ************************************************************************* */
void VectorValues::insert(Index j, const Vector& value) {
  // Make sure j does not already exist
  if(exists(j))
    throw invalid_argument("VectorValues: requested variable index to insert already exists.");

  // If this adds variables at the end, insert zero-length entries up to j
  if(j >= size())
    values_.resize(j+1);

  // Assign value
  values_[j] = value;
}

/* ************************************************************************* */
void VectorValues::print(const std::string& str, const IndexFormatter& formatter) const {
  std::cout << str << ": " << size() << " elements\n";
  for (Index var = 0; var < size(); ++var)
    std::cout << "  " << formatter(var) << ": \n" << (*this)[var] << "\n";
  std::cout.flush();
}

/* ************************************************************************* */
bool VectorValues::equals(const VectorValues& x, double tol) const {
  if(this->size() != x.size())
    return false;
  for(Index j=0; j < size(); ++j)
    if(!equal_with_abs_tol(values_[j], x.values_[j], tol))
      return false;
  return true;
}

/* ************************************************************************* */
void VectorValues::resize(Index nVars, size_t varDim) {
  values_.resize(nVars);
  for(Index j = 0; j < nVars; ++j)
    values_[j] = Vector(varDim);
}

/* ************************************************************************* */
void VectorValues::resizeLike(const VectorValues& other) {
  values_.resize(other.size());
  for(Index j = 0; j < other.size(); ++j)
    values_[j].resize(other.values_[j].size());
}

/* ************************************************************************* */
VectorValues VectorValues::SameStructure(const VectorValues& other) {
  VectorValues ret;
  ret.resizeLike(other);
  return ret;
}

/* ************************************************************************* */
VectorValues VectorValues::Zero(Index nVars, size_t varDim) {
  VectorValues ret(nVars, varDim);
  ret.setZero();
  return ret;
}

/* ************************************************************************* */
void VectorValues::setZero() {
  BOOST_FOREACH(Vector& v, *this) {
    v.setZero();
  }
}

/* ************************************************************************* */
const Vector VectorValues::asVector() const {
  return internal::extractVectorValuesSlices(*this,
    boost::make_counting_iterator(size_t(0)), boost::make_counting_iterator(this->size()), true);
}

/* ************************************************************************* */
const Vector VectorValues::vector(const std::vector<Index>& indices) const {
  return internal::extractVectorValuesSlices(*this, indices.begin(), indices.end());
}

/* ************************************************************************* */
bool VectorValues::hasSameStructure(const VectorValues& other) const {
  if(this->size() != other.size())
    return false;
  for(size_t j = 0; j < size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    if(this->values_[j].rows() != other.values_[j].rows())
      return false;
  return true;
}

/* ************************************************************************* */
void VectorValues::permuteInPlace(const Permutation& permutation) {
  // Allocate new values
  Values newValues(this->size());

  // Swap values from this VectorValues to the permuted VectorValues
  for(size_t i = 0; i < this->size(); ++i)
    newValues[i].swap(this->at(permutation[i]));

  // Swap the values into this VectorValues
  values_.swap(newValues);
}

/* ************************************************************************* */
void VectorValues::permuteInPlace(const Permutation& selector, const Permutation& permutation) {
  if(selector.size() != permutation.size())
    throw invalid_argument("VariableIndex::permuteInPlace (partial permutation version) called with selector and permutation of different sizes.");
  // Create new index the size of the permuted entries
  Values reorderedEntries(selector.size());
  // Permute the affected entries into the new index
  for(size_t dstSlot = 0; dstSlot < selector.size(); ++dstSlot)
    reorderedEntries[dstSlot].swap(values_[selector[permutation[dstSlot]]]);
  // Put the affected entries back in the new order
  for(size_t slot = 0; slot < selector.size(); ++slot)
    values_[selector[slot]].swap(reorderedEntries[slot]);
}

/* ************************************************************************* */
void VectorValues::swap(VectorValues& other) {
  this->values_.swap(other.values_);
}

/* ************************************************************************* */
double VectorValues::dot(const VectorValues& v) const {
  double result = 0.0;
  if(this->size() != v.size())
    throw invalid_argument("VectorValues::dot called with different vector sizes");
  for(Index j = 0; j < this->size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    if(this->values_[j].size() == v.values_[j].size())
      result += this->values_[j].dot(v.values_[j]);
    else
      throw invalid_argument("VectorValues::dot called with different vector sizes");
  return result;
}

/* ************************************************************************* */
double VectorValues::norm() const {
  return std::sqrt(this->squaredNorm());
}

/* ************************************************************************* */
double VectorValues::squaredNorm() const {
  double sumSquares = 0.0;
  for(Index j = 0; j < this->size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    sumSquares += this->values_[j].squaredNorm();
  return sumSquares;
}

/* ************************************************************************* */
VectorValues VectorValues::operator+(const VectorValues& c) const {
  VectorValues result = SameStructure(*this);
  if(this->size() != c.size())
    throw invalid_argument("VectorValues::operator+ called with different vector sizes");
  for(Index j = 0; j < this->size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    if(this->values_[j].size() == c.values_[j].size())
      result.values_[j] = this->values_[j] + c.values_[j];
    else
      throw invalid_argument("VectorValues::operator- called with different vector sizes");
  return result;
}

/* ************************************************************************* */
VectorValues VectorValues::operator-(const VectorValues& c) const {
  VectorValues result = SameStructure(*this);
  if(this->size() != c.size())
    throw invalid_argument("VectorValues::operator- called with different vector sizes");
  for(Index j = 0; j < this->size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    if(this->values_[j].size() == c.values_[j].size())
      result.values_[j] = this->values_[j] - c.values_[j];
    else
      throw invalid_argument("VectorValues::operator- called with different vector sizes");
  return result;
}

/* ************************************************************************* */
void VectorValues::operator+=(const VectorValues& c) {
  if(this->size() != c.size())
    throw invalid_argument("VectorValues::operator+= called with different vector sizes");
  for(Index j = 0; j < this->size(); ++j)
    // Directly accessing maps instead of using VV::dim in case some values are empty
    if(this->values_[j].size() == c.values_[j].size())
      this->values_[j] += c.values_[j];
    else
      throw invalid_argument("VectorValues::operator+= called with different vector sizes");
}

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

} // \namespace gtsam


#define BOOST_TEST_MODULE HYPERGRAPH_TEST
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include "hypergraph.h"
#include "rand_gen.h"

using namespace minipart;

BOOST_AUTO_TEST_SUITE(hypergraph)

BOOST_AUTO_TEST_CASE(degree2) {
  typedef Hypergraph H;
  H::Builder b;

  std::vector<Node> nodes;
  for (unsigned i = 0; i < 5; ++i) {
    nodes.push_back(b.addNode());
  }

  std::vector<Edge> edges;
  for (unsigned i = 0; i < 5; ++i) {
    for (unsigned j = i+1; j < 5; ++j) {
      Edge e = b.addEdge({nodes[i], nodes[j]}, edges.size());
      edges.push_back(e);
    }
  }

  H g = b;
  g.checkConsistency();

  BOOST_CHECK_EQUAL (g.nEdges(), 10u);
  BOOST_CHECK_EQUAL (g.nNodes(), 5u);
  BOOST_CHECK (edges.size() == g.nEdges());
  BOOST_CHECK (nodes.size() == g.nNodes());
  for (auto e : edges) {
    BOOST_CHECK_EQUAL (g.weight(e), e.id);
    BOOST_CHECK_EQUAL (g.nodes(e).size(), 2lu);
  }
  for (auto n : g.nodes()) {
    BOOST_CHECK_EQUAL (g.edges(n).size(), 4lu);
  }
}

BOOST_AUTO_TEST_CASE(degree3) {
  typedef Hypergraph H;
  H::Builder b;

  std::vector<Node> nodes;
  for (unsigned i = 0; i < 5; ++i) {
    nodes.push_back(b.addNode());
  }

  std::vector<Edge> edges;
  for (unsigned i = 0; i < 5; ++i) {
    for (unsigned j = i+1; j < 5; ++j) {
      for (unsigned k = j+1; k < 5; ++k) {
        Edge e = b.addEdge({nodes[i], nodes[j], nodes[k]}, edges.size());
        edges.push_back(e);
      }
    }
  }

  H g = b;
  g.checkConsistency();

  BOOST_CHECK_EQUAL (g.nEdges(), 10u);
  BOOST_CHECK_EQUAL (g.nNodes(), 5u);
  for (auto e : edges) {
    BOOST_CHECK_EQUAL (g.weight(e), e.id);
    BOOST_CHECK_EQUAL (g.nodes(e).size(), 3lu);
  }
  for (auto n : g.nodes()) {
    BOOST_CHECK_EQUAL (g.edges(n).size(), 6lu);
  }
}

BOOST_AUTO_TEST_CASE(randGen) {
  typedef Hypergraph H;
  std::minstd_rand rgen;

  const unsigned nNodes = 100;
  const unsigned nEdges = 1000;
  H::Builder b(nNodes);
  addRandomEdges(b, nEdges, 3.0, 10.0, rgen);
  H g = b;
  g.checkConsistency();

  BOOST_CHECK (g.nNodes() == nNodes);
  BOOST_CHECK (g.nEdges() >= nEdges * 0.95);
  for (auto e : g.edges()) {
    BOOST_CHECK (g.nodes(e).size() >= 2lu);
  }
}

BOOST_AUTO_TEST_SUITE_END()