/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "add_stg.hpp"

#include <numeric>

#include <boost/random/detail/integer_log2.hpp>

#include <core/utils/range_utils.hpp>
#include <reversible/functions/add_gates.hpp>
#include <reversible/target_tags.hpp>
#include <reversible/utils/permutation.hpp>

namespace cirkit
{

/******************************************************************************
 * Types                                                                      *
 ******************************************************************************/

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

void add_stg_as_other( circuit& circ, const tt& func, const tt& func_real, const std::vector<unsigned>& line_map )
{
  const auto num_vars = tt_num_vars( func );

  std::vector<kitty::detail::spectral_operation> trans;
  const auto func_norm = kitty::exact_spectral_canonization( to_kitty( func ),
                                                             [&trans]( const std::vector<kitty::detail::spectral_operation>& ops ) {
                                                               std::copy( ops.begin(), ops.end(), std::back_inserter( trans ) );
                                                             } );
  const auto func_real_norm = kitty::exact_spectral_canonization( to_kitty( func_real ),
                                                                  [&trans]( const std::vector<kitty::detail::spectral_operation>& ops ) {
                                                                    std::copy( ops.rbegin(), ops.rend(), std::back_inserter( trans ) );
                                                                  } );

  assert( func_norm == func_real_norm );

  std::vector<unsigned> line( num_vars + 1u );
  if ( !line_map.empty() )
  {
    std::copy( line_map.begin(), line_map.end(), line.begin() );
  }
  else
  {
    std::iota( line.begin(), line.end(), 0u );
  }

  auto index = circ.num_gates();

  /* we defer the adding of NOT gates to the end, but NOT gates are just stored
     and used to adjust the polarities of the controls.
     We also try to merge an output inversion into the control line of a disjoint
     transformation. */
  boost::dynamic_bitset<> not_mask( num_vars + 1 );

  /* we also defer adding SWAP gates to the end.  The swaps are stored in a permutation
     that is used when other gates are added.  Finally the SWAP gates are computed by
     decomposing the permutation into transpositions. */
  permutation_t perm = identity_permutation( num_vars );

  for ( const auto& t : trans )
  {
    switch ( t._kind )
    {
    default:
      assert( false );
    case kitty::detail::spectral_operation::kind::permutation:
      std::swap( perm[boost::integer_log2( t._var1 )], perm[boost::integer_log2( t._var2 )] );
      break;
    case kitty::detail::spectral_operation::kind::input_negation:
      not_mask.flip( perm[boost::integer_log2( t._var1 )] );
      break;
    case kitty::detail::spectral_operation::kind::output_negation:
      not_mask.flip( num_vars );
      break;
    case kitty::detail::spectral_operation::kind::spectral_translation:
    {
      const auto v1 = perm[boost::integer_log2( t._var1 )];
      const auto v2 = perm[boost::integer_log2( t._var2 )];
      insert_cnot( circ, index, make_var( line[v2], !not_mask.test( v2 ) ), line[v1] );
      insert_cnot( circ, index, make_var( line[v2], !not_mask.test( v2 ) ), line[v1] );
      ++index;
    }
    break;
    case kitty::detail::spectral_operation::kind::disjoint_translation:
    { /* disjoint transformation of output */
      const auto v1 = perm[boost::integer_log2( t._var1 )];

      auto pol = !not_mask.test( v1 );
      if ( not_mask.test( num_vars ) )
      {
        not_mask.reset( num_vars );
        pol = !pol;
      }
      insert_cnot( circ, index, make_var( line[v1], pol ), line[num_vars] );
    }
    break;
    }
  }

  /* add SWAPS */
  const auto perm_inv = permutation_invert( perm );
  for ( const auto& trans : permutation_to_transpositions( perm_inv ) )
  {
    insert_fredkin( circ, index, {}, line[trans.first], line[trans.second] );
    insert_fredkin( circ, index, {}, line[trans.first], line[trans.second] );
    ++index;
  }

  /* add inverters from mask */
  for ( auto i = 0u; i < num_vars; ++i )
  {
    if ( not_mask.test( i ) )
    {
      insert_not( circ, index, line[perm_inv[i]] );
      insert_not( circ, index, line[perm_inv[i]] );
      ++index;
    }
  }
  if ( not_mask.test( num_vars ) )
  {
    insert_not( circ, index, line[num_vars] );
  }

  /* add middle gate */
  auto& g = circ.insert_gate( index );
  g.set_type( stg_tag( func_real ) );
  for ( auto i = 0u; i < num_vars; ++i )
  {
    g.add_control( make_var( line[i], true ) );
  }
  g.add_target( line[num_vars] );
}
}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


/*--------------------------------------------------------------------------*\
 |                                                                          |
 |  This program is free software; you can redistribute it and/or modify    |
 |  it under the terms of the GNU General Public License as published by    |
 |  the Free Software Foundation; either version 2, or (at your option)     |
 |  any later version.                                                      |
 |                                                                          |
 |  This program is distributed in the hope that it will be useful,         |
 |  but WITHOUT ANY WARRANTY; without even the implied warranty of          |
 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
 |  GNU General Public License for more details.                            |
 |                                                                          |
 |  You should have received a copy of the GNU General Public License       |
 |  along with this program; if not, write to the Free Software             |
 |  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               |
 |                                                                          |
 |  Copyright (C) 2003                                                      |
 |                                                                          |
 |         , __                 , __                                        |
 |        /|/  \               /|/  \                                       |
 |         | __/ _   ,_         | __/ _   ,_                                |
 |         |   \|/  /  |  |   | |   \|/  /  |  |   |                        |
 |         |(__/|__/   |_/ \_/|/|(__/|__/   |_/ \_/|/                       |
 |                           /|                   /|                        |
 |                           \|                   \|                        |
 |                                                                          |
 |      Enrico Bertolazzi                                                   |
 |      Dipartimento di Ingegneria Meccanica e Strutturale                  |
 |      Universita` degli Studi di Trento                                   |
 |      Via Mesiano 77, I-38050 Trento, Italy                               |
 |      email: enrico.bertolazzi@unitn.it                                   |
 |                                                                          |
\*--------------------------------------------------------------------------*/

/*
  http://www.sfu.ca/~ssurjano/optimization.html
  http://www-optima.amp.i.kyoto-u.ac.jp/member/student/hedar/Hedar_files/TestGO_files/Page364.htm
*/

#ifndef TESTS_NONLIN_HH
#define TESTS_NONLIN_HH

#include <iostream>
#include <sstream>

#include <string>
#include <vector>
#include <cstdint>
#include <cstdio>
#include <cmath>
#include <map>

#define DEBUG 1
#define EIGEN_NO_AUTOMATIC_RESIZING 1

#include <Utils.hh>
#include <Eigen/Core>
#include <Eigen/Dense>

namespace NLproblem {

  typedef std::basic_ostream<char> ostream_type;

  using std::string;
  using std::map;
  using std::vector;
  using std::pair;
  using std::max;
  using std::min;

  using std::numeric_limits;

  typedef double  real_type;
  typedef int32_t integer;

  //! `m_e` the value of \f$ e \f$.
  static real_type const m_e = 2.718281828459045235360287471352662497757;

  //! `m_pi` the value of \f$ \pi \f$.
  static real_type const m_pi = 3.141592653589793238462643383279502884197;

  //! `m_2pi` the value of \f$ 2\pi \f$.
  static real_type const m_2pi = 6.283185307179586476925286766559005768394;

  //! `m_pi_2` the value of \f$ \pi/2 \f$.
  static real_type const m_pi_2 = 1.570796326794896619231321691639751442098;

  //! `m_pi_4` the value of \f$ \pi/4 \f$.
  static real_type const m_pi_4 = 0.7853981633974483096156608458198757210492;

  //! `m_1_pi` the value of \f$ 1/\pi \f$.
  static real_type const m_1_pi = 0.3183098861837906715377675267450287240689;

  //! `m_2_pi` the value of \f$ 2/\pi \f$.
  static real_type const m_2_pi = 0.6366197723675813430755350534900574481378;

  //! `m_sqrtpi` the value of \f$ \sqrt{\pi} \f$.
  static real_type const m_sqrtpi = 1.772453850905516027298167483341145182798;

  //! `m_2_sqrtpi` the value of \f$ 2/\sqrt{\pi} \f$.
  static real_type const m_2_sqrtpi = 1.128379167095512573896158903121545171688;

  //! `m_sqrt2` the value of \f$ \sqrt{2} \f$.
  static real_type const m_sqrt2 = 1.414213562373095048801688724209698078570;

  //! `m_1_sqrt2` the value of \f$ 1/\sqrt{2} \f$.
  static real_type const m_1_sqrt2 = 0.7071067811865475244008443621048490392850;

  static real_type real_max = numeric_limits<real_type>::max();

  typedef Eigen::Matrix<real_type,Eigen::Dynamic,Eigen::Dynamic> dmat_t;
  typedef Eigen::Matrix<real_type,Eigen::Dynamic,1>              dvec_t;
  typedef Eigen::Matrix<integer,Eigen::Dynamic,1>               ivec_t;

  class nonlinearBase {

    string const _title;
    string const _bibtex;

    nonlinearBase();
    nonlinearBase( nonlinearBase const & );
    nonlinearBase const & operator = ( nonlinearBase const & );

  protected:

    // U T I L I T Y  F U N C T I O N S -----------------------------------------

    real_type power2( real_type a ) const { return a*a; }
    real_type power3( real_type a ) const { return a*a*a; }
    real_type power4( real_type a ) const { real_type a2 = a*a; return a2*a2; }
    real_type power5( real_type a ) const { real_type a2 = a*a; return a2*a2*a; }
    real_type power6( real_type a ) const { real_type a2 = a*a; return a2*a2*a2; }

    void
    checkMinEquations( integer i, integer i_min ) const {
      UTILS_ASSERT(
        i >= i_min, "checkMinEquations:: i = {} < {}", i, i_min
      );
    }

    void
    checkEven( integer i, integer i_min ) const {
      UTILS_ASSERT(
        (i % 2) == 0 && i >= i_min, "checkEven:: odd index i = {}", i
      );
    }

    void
    checkOdd( integer i, integer i_min ) const {
      UTILS_ASSERT(
        (i % 2) != 0 && i >= i_min, "checkOdd:: odd index i = {}", i
      );
    }

    void
    checkThree( integer i, integer i_min ) const {
      UTILS_ASSERT(
        (i % 3) == 0 && i >= i_min, "checkThree:: index i = {}", i
      );
    }

    void
    checkFour( integer i, integer i_min ) const {
      UTILS_ASSERT(
        (i % 4) == 0 && i >= i_min, "checkFour:: index i = {}", i
      );
    }

  public:

    explicit nonlinearBase( string const & t, string const & b )
    : _title( t )
    , _bibtex( b )
    {}

    virtual ~nonlinearBase() {}

    string const & bibtex() const { return _bibtex; }
    string const & title()  const { return _title; }

  };

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  class multivariateFunction : public nonlinearBase {

    multivariateFunction( multivariateFunction const & );
    multivariateFunction const & operator = ( multivariateFunction const & );

  protected:
    integer n;

  public:

    multivariateFunction( string const & t, string const & b, integer _n )
    : nonlinearBase( fmt::format("{} neq = {}", t, _n ), b )
    , n(_n)
    { }

    virtual ~multivariateFunction() {}

    virtual real_type eval( dvec_t const & x ) const = 0;
    virtual void      gradient( dvec_t const & x, dvec_t & g ) const = 0;

    virtual integer hessianNnz() const = 0;
    virtual void    hessian( dvec_t const & x, dvec_t & jac ) const = 0;
    virtual void    hessianPattern( ivec_t & i, ivec_t & j ) const = 0;

    virtual integer numExactSolution() const = 0;
    virtual void    getExactSolution( dvec_t & x, integer idx ) const = 0;

    virtual integer numInitialPoint() const = 0;
    virtual void    getInitialPoint( dvec_t & x, integer idx ) const = 0;

    virtual void    checkIfAdmissible( dvec_t const & x ) const = 0;

    virtual void
    boundingBox( dvec_t & L, dvec_t & U ) const {
      L.fill( -real_max );
      U.fill( real_max );
    }

    integer dimX( void ) const { return n; }

  };

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  class nonlinearLeastSquares: public nonlinearBase {

    nonlinearLeastSquares( nonlinearLeastSquares const & );
    nonlinearLeastSquares const & operator = ( nonlinearLeastSquares const & );

  protected:

    integer n, m;

  public:

    nonlinearLeastSquares(
      string const & t,
      string const & b,
      integer       _n,
      integer       _m
    )
    : nonlinearBase(
        fmt::format( "{} dimF = {}, dimX = {}", t, _n, _m ), b
      )
    , n(_n)
    , m(_m)
    {}

    virtual ~nonlinearLeastSquares() {}

    virtual real_type evalFk( dvec_t const & x, integer k ) const = 0;
    virtual void      evalF ( dvec_t const & x, dvec_t & f ) const = 0;

    virtual integer jacobianNnz() const = 0;
    virtual void    jacobian( dvec_t const & x, dvec_t & jac ) const = 0;
    virtual void    jacobianPattern( ivec_t & i, ivec_t & j ) const = 0;

    virtual integer tensorNnz() const = 0;
    virtual void    tensor( dvec_t const & x, dvec_t const & lambda, dvec_t & jac ) const = 0;
    virtual void    tensorPattern( ivec_t & i, ivec_t & j ) const = 0;

    virtual integer numExactSolution() const = 0;
    virtual void    getExactSolution( dvec_t & x, integer idx ) const = 0;

    virtual integer numInitialPoint() const = 0;
    virtual void    getInitialPoint( dvec_t & x, integer idx ) const = 0;

    virtual void checkIfAdmissible( dvec_t const & x ) const = 0;

    virtual void
    boundingBox( dvec_t & L, dvec_t & U ) const {
      L.fill( -real_max );
      U.fill( real_max );
    }

    integer dimF( void ) const { return n; }
    integer dimX( void ) const { return m; }

  };

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  class nonlinearSystem: public nonlinearBase {

    nonlinearSystem( nonlinearSystem const & );
    nonlinearSystem const & operator = ( nonlinearSystem const & );

  protected:

    // U T I L I T Y  F U N C T I O N S -----------------------------------------

    // n = dimensione matrice
    // i = riga
    // j = colonna
    // indirizzamento fortran
    integer faddr( integer i, integer j ) const
    { return (i-1) + (j-1) * n; }

    integer caddr( integer i, integer j ) const
    { return i + j * n; }

    integer n;

  public:

    nonlinearSystem( string const & t, string const & b, integer _n )
    : nonlinearBase( fmt::format("{} neq = {}", t, _n ), b )
    , n(_n)
    { }

    virtual ~nonlinearSystem() {}

    //void
    //setup( string const & t, integer _n )
    //{ theTitle = t; n = _n; }

    virtual real_type evalFk( dvec_t const & x, integer k ) const = 0;
    virtual void      evalF ( dvec_t const & x, dvec_t & f ) const = 0;

    virtual integer jacobianNnz() const = 0;
    virtual void    jacobian( dvec_t const & x, dvec_t & jac ) const = 0;
    virtual void    jacobianPattern( ivec_t & i, ivec_t & j ) const = 0;

    virtual integer numExactSolution() const = 0;
    virtual void    getExactSolution( dvec_t & x, integer idx ) const = 0;

    virtual integer numInitialPoint() const = 0;
    virtual void    getInitialPoint( dvec_t & x, integer idx ) const = 0;

    virtual void checkIfAdmissible( dvec_t const & x ) const = 0;

    virtual void
    boundingBox( dvec_t & L, dvec_t & U ) const {
      L.fill( -real_max );
      U.fill( real_max );
    }

    integer numEqns( void ) const { return n; }

    integer
    fill_CSR(
      dvec_t const & x,
      ivec_t       & R,
      ivec_t       & J,
      dvec_t       & values
    ) const;

  };

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  class nonlinearSystemFromMultivariateFunction: public nonlinearSystem {

    nonlinearSystemFromMultivariateFunction(
      nonlinearSystemFromMultivariateFunction const &
    );
    nonlinearSystemFromMultivariateFunction const &
    operator = (nonlinearSystemFromMultivariateFunction const &);

    multivariateFunction const * pMF;

  public:

    nonlinearSystemFromMultivariateFunction(
      multivariateFunction const * _pMF
    )
    : nonlinearSystem( _pMF->title(), _pMF->bibtex(), _pMF->dimX() )
    , pMF(_pMF) {
    }

    virtual ~nonlinearSystemFromMultivariateFunction() {}

    virtual
    real_type
    evalFk( dvec_t const & x, integer k ) const {
      dvec_t g(n);
      evalF( x, g );
      return g(k);
    }

    virtual
    void
    evalF( dvec_t const & x, dvec_t & g ) const {
      pMF->gradient( x, g );
    }

    virtual
    integer
    jacobianNnz() const
    { return pMF->hessianNnz(); }

    virtual
    void
    jacobian( dvec_t const & x, dvec_t & hess ) const
    { pMF->hessian(x,hess); }

    virtual
    void
    jacobianPattern( ivec_t & i, ivec_t & j ) const
    { pMF->hessianPattern( i, j ); }

    virtual
    integer
    numExactSolution() const
    { return pMF->numExactSolution(); }

    virtual
    void
    getExactSolution( dvec_t & x, integer idx ) const
    { pMF->getExactSolution( x, idx ); }

    virtual
    integer
    numInitialPoint() const
    { return pMF->numInitialPoint(); }

    virtual
    void
    getInitialPoint( dvec_t & x, integer idx ) const
    { pMF->getInitialPoint( x, idx ); }

    virtual
    void
    checkIfAdmissible( dvec_t const & x ) const
    { pMF->checkIfAdmissible( x ); }

    virtual
    void
    boundingBox( dvec_t & L, dvec_t & U ) const
    { pMF->boundingBox( L, U ); }

    integer numEqns( void ) const { return pMF->dimX(); }

    string const & title(void) const { return pMF->title(); }

  };

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#if 0
  class nonlinearSystemFromLeastSquares: public nonlinearSystem {

    nonlinearSystemFromLeastSquares(nonlinearSystemFromLeastSquares const &);
    nonlinearSystemFromLeastSquares const &
    operator = (nonlinearSystemFromLeastSquares const &);

    nonlinearLeastSquares const * pLS;

  public:

    nonlinearSystemFromLeastSquares( nonlinearLeastSquares const * _pLS )
    : nonlinearSystem( _pLS->title(), _pLS->dimX() )
    , pLS(_pLS) {
    }

    virtual ~nonlinearSystemFromLeastSquares() {}

    virtual
    real_type
    evalFk( dvec_t const & x, integer k ) const {
      dvec_t g(n);
      evalF( x, g );
      return g(k);
    }

    virtual
    void
    evalF( dvec_t const & x, dvec_t & g ) const {
      pLS->gradient( x, g );
    }

    virtual
    integer
    jacobianNnz() const
    { return pMF->hessianNnz(); }

    virtual
    void
    jacobian( dvec_t const & x, dmat_t & hess ) const
    { pMF->hessian(x,hess); }

    virtual
    void
    jacobianPattern( ivec_t & i, ivec_t & j ) const
    { pMF->hessianPattern(i,j); }

    virtual
    integer
    numExactSolution() const
    { return pMF->numExactSolution(); }

    virtual
    void
    getExactSolution( dvec_t & x, integer idx ) const
    { pMF->getExactSolution(x,idx); }

    virtual
    integer
    numInitialPoint() const
    { return pMF->numInitialPoint(); }

    virtual
    void
    getInitialPoint( dvec_t & x, integer idx ) const
    { pMF->getInitialPoint(x,idx); }

    virtual
    void
    checkIfAdmissible( dvec_t const & x ) const
    { pMF->checkIfAdmissible(x); }

    virtual
    void
    boundingBox( dvec_t & L, dvec_t & U ) const
    { pMF->boundingBox(L,U); }

    integer numEqns( void ) const { return pMF->dimX(); }

    string const & title(void) const { return pMF->title(); }

  };
#endif

  extern vector<nonlinearSystem*> theProblems;
  extern map<string,integer>      theProblemsMap;
  void initProblems();

}

#endif


//=======================================================================
// Copyright 2013 University of Warsaw.
// Authors: Piotr Wygocki
//
// 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)
//=======================================================================
//
// This algorithm is described in "Network Flows: Theory, Algorithms, and
// Applications"
// by Ahuja, Magnanti, Orlin.

#ifndef BOOST_GRAPH_SUCCESSIVE_SHORTEST_PATH_HPP
#define BOOST_GRAPH_SUCCESSIVE_SHORTEST_PATH_HPP

#include <numeric>

#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/detail/augment.hpp>

namespace boost
{

namespace detail
{

    template < class Graph, class Weight, class Distance, class Reversed >
    class MapReducedWeight
    : public put_get_helper< typename property_traits< Weight >::value_type,
          MapReducedWeight< Graph, Weight, Distance, Reversed > >
    {
        typedef graph_traits< Graph > gtraits;

    public:
        typedef boost::readable_property_map_tag category;
        typedef typename property_traits< Weight >::value_type value_type;
        typedef value_type reference;
        typedef typename gtraits::edge_descriptor key_type;
        MapReducedWeight(const Graph& g, Weight w, Distance d, Reversed r)
        : g_(g), weight_(w), distance_(d), rev_(r)
        {
        }

        reference operator[](key_type v) const
        {
            return get(distance_, source(v, g_)) - get(distance_, target(v, g_))
                + get(weight_, v);
        }

    private:
        const Graph& g_;
        Weight weight_;
        Distance distance_;
        Reversed rev_;
    };

    template < class Graph, class Weight, class Distance, class Reversed >
    MapReducedWeight< Graph, Weight, Distance, Reversed > make_mapReducedWeight(
        const Graph& g, Weight w, Distance d, Reversed r)
    {
        return MapReducedWeight< Graph, Weight, Distance, Reversed >(
            g, w, d, r);
    }

} // detail

template < class Graph, class Capacity, class ResidualCapacity, class Reversed,
    class Pred, class Weight, class Distance, class Distance2,
    class VertexIndex >
void successive_shortest_path_nonnegative_weights(const Graph& g,
    typename graph_traits< Graph >::vertex_descriptor s,
    typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
    ResidualCapacity residual_capacity, Weight weight, Reversed rev,
    VertexIndex index, Pred pred, Distance distance, Distance2 distance_prev)
{
    filtered_graph< const Graph, is_residual_edge< ResidualCapacity > > gres
        = detail::residual_graph(g, residual_capacity);
    typedef typename graph_traits< Graph >::edge_descriptor edge_descriptor;

    BGL_FORALL_EDGES_T(e, g, Graph)
    {
        put(residual_capacity, e, get(capacity, e));
    }

    BGL_FORALL_VERTICES_T(v, g, Graph) { put(distance_prev, v, 0); }

    while (true)
    {
        BGL_FORALL_VERTICES_T(v, g, Graph) { put(pred, v, edge_descriptor()); }
        dijkstra_shortest_paths(gres, s,
            weight_map(
                detail::make_mapReducedWeight(gres, weight, distance_prev, rev))
                .distance_map(distance)
                .vertex_index_map(index)
                .visitor(make_dijkstra_visitor(
                    record_edge_predecessors(pred, on_edge_relaxed()))));

        if (get(pred, t) == edge_descriptor())
        {
            break;
        }

        BGL_FORALL_VERTICES_T(v, g, Graph)
        {
            put(distance_prev, v, get(distance_prev, v) + get(distance, v));
        }

        detail::augment(g, s, t, pred, residual_capacity, rev);
    }
}

// in this namespace argument dispatching tak place
namespace detail
{

    template < class Graph, class Capacity, class ResidualCapacity,
        class Weight, class Reversed, class Pred, class Distance,
        class Distance2, class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch3(const Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, Pred pred, Distance dist, Distance2 dist_pred)
    {
        successive_shortest_path_nonnegative_weights(g, s, t, capacity,
            residual_capacity, weight, rev, index, pred, dist, dist_pred);
    }

    // setting default distance map
    template < class Graph, class Capacity, class ResidualCapacity,
        class Weight, class Reversed, class Pred, class Distance,
        class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch3(Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, Pred pred, Distance dist, param_not_found)
    {
        typedef typename property_traits< Weight >::value_type D;

        std::vector< D > d_map(num_vertices(g));

        successive_shortest_path_nonnegative_weights(g, s, t, capacity,
            residual_capacity, weight, rev, index, pred, dist,
            make_iterator_property_map(d_map.begin(), index));
    }

    template < class Graph, class P, class T, class R, class Capacity,
        class ResidualCapacity, class Weight, class Reversed, class Pred,
        class Distance, class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch2(Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, Pred pred, Distance dist,
        const bgl_named_params< P, T, R >& params)
    {
        successive_shortest_path_nonnegative_weights_dispatch3(g, s, t,
            capacity, residual_capacity, weight, rev, index, pred, dist,
            get_param(params, vertex_distance2));
    }

    // setting default distance map
    template < class Graph, class P, class T, class R, class Capacity,
        class ResidualCapacity, class Weight, class Reversed, class Pred,
        class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch2(Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, Pred pred, param_not_found,
        const bgl_named_params< P, T, R >& params)
    {
        typedef typename property_traits< Weight >::value_type D;

        std::vector< D > d_map(num_vertices(g));

        successive_shortest_path_nonnegative_weights_dispatch3(g, s, t,
            capacity, residual_capacity, weight, rev, index, pred,
            make_iterator_property_map(d_map.begin(), index),
            get_param(params, vertex_distance2));
    }

    template < class Graph, class P, class T, class R, class Capacity,
        class ResidualCapacity, class Weight, class Reversed, class Pred,
        class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch1(Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, Pred pred, const bgl_named_params< P, T, R >& params)
    {
        successive_shortest_path_nonnegative_weights_dispatch2(g, s, t,
            capacity, residual_capacity, weight, rev, index, pred,
            get_param(params, vertex_distance), params);
    }

    // setting default predecessors map
    template < class Graph, class P, class T, class R, class Capacity,
        class ResidualCapacity, class Weight, class Reversed,
        class VertexIndex >
    void successive_shortest_path_nonnegative_weights_dispatch1(Graph& g,
        typename graph_traits< Graph >::vertex_descriptor s,
        typename graph_traits< Graph >::vertex_descriptor t, Capacity capacity,
        ResidualCapacity residual_capacity, Weight weight, Reversed rev,
        VertexIndex index, param_not_found,
        const bgl_named_params< P, T, R >& params)
    {
        typedef typename graph_traits< Graph >::edge_descriptor edge_descriptor;
        std::vector< edge_descriptor > pred_vec(num_vertices(g));

        successive_shortest_path_nonnegative_weights_dispatch2(g, s, t,
            capacity, residual_capacity, weight, rev, index,
            make_iterator_property_map(pred_vec.begin(), index),
            get_param(params, vertex_distance), params);
    }

} // detail

template < class Graph, class P, class T, class R >
void successive_shortest_path_nonnegative_weights(Graph& g,
    typename graph_traits< Graph >::vertex_descriptor s,
    typename graph_traits< Graph >::vertex_descriptor t,
    const bgl_named_params< P, T, R >& params)
{

    return detail::successive_shortest_path_nonnegative_weights_dispatch1(g, s,
        t,
        choose_const_pmap(get_param(params, edge_capacity), g, edge_capacity),
        choose_pmap(get_param(params, edge_residual_capacity), g,
            edge_residual_capacity),
        choose_const_pmap(get_param(params, edge_weight), g, edge_weight),
        choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse),
        choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
        get_param(params, vertex_predecessor), params);
}

template < class Graph >
void successive_shortest_path_nonnegative_weights(Graph& g,
    typename graph_traits< Graph >::vertex_descriptor s,
    typename graph_traits< Graph >::vertex_descriptor t)
{
    bgl_named_params< int, buffer_param_t > params(0);
    successive_shortest_path_nonnegative_weights(g, s, t, params);
}

} // boost
#endif /* BOOST_GRAPH_SUCCESSIVE_SHORTEST_PATH_HPP */


#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <chrono>
#include <limits>
#include <float.h>

#include <CGAL/Polygon_mesh_processing/measure.h>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>

#include "Utils.h"
#include "OBJ_printer.h"
#include "SymMat.h"
#include "cluster_settings.h"
#include "ErrorQuadric.h"
#include "Chart.h"
#include "JoinOperation.h"
#include "polyhedron_builder.h"
#include "eig.h"

#include "CGAL_typedefs.h"

#define SEPARATE_CHART_FILE false

Vector normalise(Vector v) {return v / std::sqrt(v.squared_length());}

//key: face_id, value: chart_id
std::map<uint32_t, uint32_t> chart_id_map;

void count_faces_in_active_charts(std::vector<Chart> &charts) {
  uint32_t active_faces = 0;
  for (auto& chart : charts)
  {
    if (chart.active)
    {
      active_faces += chart.facets.size();
    }
  }
  std::cout << "found " << active_faces << " active faces\n";
}

uint32_t
create_charts (Polyhedron &P, const double cost_threshold , const uint32_t chart_threshold, CLUSTER_SETTINGS cluster_settings){
  std::stringstream report;

  //calculate areas of each face
  std::cout << "Calculating face areas...\n";
  std::map<face_descriptor,double> fareas;
  for(face_descriptor fd: faces(P)){
    fareas[fd] = CGAL::Polygon_mesh_processing::face_area  (fd,P);
  }
  //calculate normals of each faces
  std::cout << "Calculating face normals...\n";
  std::map<face_descriptor,Vector> fnormals;
  CGAL::Polygon_mesh_processing::compute_face_normals(P,boost::make_assoc_property_map(fnormals));

  //get boost face iterator
  face_iterator fb_boost, fe_boost;
  boost::tie(fb_boost, fe_boost) = faces(P);

  //each face begins as its own chart
  std::cout << "Creating initial charts...\n";
  std::vector<Chart> charts;
  for ( Facet_iterator fb = P.facets_begin(); fb != P.facets_end(); ++fb){
    //assign id to face
    fb->id() = charts.size();

    // std::cout << "normal " << charts.size() << ": " << fnormals[*fb_boost] << std::endl;

    //init chart instance for face
    Chart c(charts.size(),*fb, fnormals[*fb_boost], fareas[*fb_boost]);
    charts.push_back(c);

    // //check uv coords saved
    // double u = fb->halfedge()->vertex()->point().get_u();
    // double v = fb->halfedge()->vertex()->point().get_v();
    // std::cout << "tex coords:  u " << u << ", v " << v << std::endl;

    fb_boost++;
  }

  //for reporting and calculating when to stop merging
  const uint32_t initial_charts = charts.size();
  const uint32_t desired_merges = initial_charts - chart_threshold;
  uint32_t chart_merges = 0;

  //create possible join list/queue. Each original edge in the mesh becomes a join (if not a boundary edge)
  std::cout << "Creating initial joins...\n";
  std::list<JoinOperation> joins;
  std::list<JoinOperation>::iterator it;

  int edgecount = 0;

  for( Edge_iterator eb = P.edges_begin(), ee = P.edges_end(); eb != ee; ++ eb){

    edgecount++;

    //only create join if halfedge is not a boundary edge
    if ( !(eb->is_border()) && !(eb->opposite()->is_border()) )
    {
          uint32_t face1 = eb->facet()->id();
          uint32_t face2 = eb->opposite()->facet()->id();
          JoinOperation join (face1,face2,JoinOperation::cost_of_join(charts[face1],charts[face2], cluster_settings));
          joins.push_back(join);
    }
  }

  std::cout << joins.size() << " joins\n" << edgecount << " edges\n";

  // join charts until target is reached
  int prev_cost_percent = -1;
  int prev_charts_percent = -1;
  int overall_percent = -1;

  joins.sort(JoinOperation::sort_joins);
  const double lowest_cost = joins.front().cost;

  //execute lowest join cost and update affected joins.  re-sort.
  std::cout << "Processing join queue...\n";
  while (joins.front().cost < cost_threshold
        &&  !joins.empty()
        &&  (charts.size() - chart_merges) > chart_threshold){

    //reporting-------------
    int percent = (int)(((joins.front().cost - lowest_cost) / (cost_threshold - lowest_cost)) * 100);
    if (percent != prev_cost_percent && percent > overall_percent) {
      prev_cost_percent = percent;
      overall_percent = percent;
      std::cout << percent << " percent complete\n";
    }
    percent = (int)(((float)chart_merges / (float)desired_merges) * 100);
    if (percent != prev_charts_percent && percent > overall_percent) {
      prev_charts_percent = percent;
      overall_percent = percent;
      std::cout << percent << " percent complete\n";
    }

    //implement the join with lowest cost
    JoinOperation join_todo = joins.front();
    joins.pop_front();

    // std::cout << "join cost : " << join_todo.cost << std::endl;

    //merge faces from chart2 into chart 1
    // std::cout << "merging charts " << join_todo.chart1_id << " and " << join_todo.chart2_id << std::endl;
    charts[join_todo.chart1_id].merge_with(charts[join_todo.chart2_id], join_todo.cost);

    //DEactivate chart 2
    if (charts[join_todo.chart2_id].active == false)
    {
      report << "chart " << join_todo.chart2_id << " was already inactive at merge " << chart_merges << std::endl; // should not happen
      continue;
    }
    charts[join_todo.chart2_id].active = false;

    int current_item = 0;
    std::list<int> to_erase;
    std::vector<JoinOperation> to_replace;

    //update itremaining joins that include either of the merged charts
    for (it = joins.begin(); it != joins.end(); ++it)
    {
      //if join is affected, update references and cost
      if (it->chart1_id == join_todo.chart1_id
         || it->chart1_id == join_todo.chart2_id
         || it->chart2_id == join_todo.chart1_id
         || it->chart2_id == join_todo.chart2_id )
      {

        //eliminate references to joined chart 2 (it is no longer active)
        // by pointing them to chart 1
        if (it->chart1_id == join_todo.chart2_id){
          it->chart1_id = join_todo.chart1_id;
        }
        if (it->chart2_id == join_todo.chart2_id){
          it->chart2_id = join_todo.chart1_id;
        }

        //search for duplicates
        if ((it->chart1_id == join_todo.chart1_id && it->chart2_id == join_todo.chart2_id)
          || (it->chart2_id == join_todo.chart1_id && it->chart1_id == join_todo.chart2_id) ){
          report << "duplicate found : c1 = " << it->chart1_id << ", c2 = " << it->chart2_id << std::endl;

          to_erase.push_back(current_item);
        }
        //check for joins within a chart
        else if (it->chart1_id == it->chart2_id)
        {
          report << "Join found within a chart: " << it->chart1_id << std::endl;
          to_erase.push_back(current_item);

        }
        else {
          //update cost with new cost
          it->cost = JoinOperation::cost_of_join(charts[it->chart1_id], charts[it->chart2_id], cluster_settings);

          //save this join to be deleted and replaced in correct position after deleting duplicates
          to_replace.push_back(*it);
          to_erase.push_back(current_item);
        }
      }
      current_item++;
    }

    //adjust ID to be deleted to account for previously deleted items
    to_erase.sort();
    int num_erased = 0;
    for (auto id : to_erase) {
      std::list<JoinOperation>::iterator it2 = joins.begin();
      std::advance(it2, id - num_erased);
      joins.erase(it2);
      num_erased++;
    }

    // replace joins that were filtered out to be sorted
    if (to_replace.size() > 0)
    {
      std::sort(to_replace.begin(), to_replace.end(), JoinOperation::sort_joins);
      std::list<JoinOperation>::iterator it2;
      uint32_t insert_item = 0;
      for (it2 = joins.begin(); it2 != joins.end(); ++it2){
        //insert items while join list item has bigger cost than element to be inserted
        while (it2->cost > to_replace[insert_item].cost
              && insert_item < to_replace.size()){
          joins.insert(it2, to_replace[insert_item]);
          insert_item++;
        }
        //if all items are in place, we are done
        if (insert_item >= to_replace.size())
        {
          break;
        }
      }
      //add any remaining items
      for (uint32_t i = insert_item; i < to_replace.size(); i++){
        joins.push_back(to_replace[i]);
      }
    }

#if 0
    //CHECK that each join would give a chart with at least 3 neighbours
    //TODO also need to check boundary edges
    to_erase.clear();
    std::vector<std::vector<uint32_t> > neighbour_count (charts.size(), std::vector<uint32_t>(0));
    std::list<JoinOperation>::iterator it2;
    for (it2 = joins.begin(); it2 != joins.end(); ++it2){
      //for chart 1 , add entry in vector for that chart containing id of chart 2
      // and vice versa
      neighbour_count[it2->chart1_id].push_back(it2->chart2_id);
      neighbour_count[it2->chart2_id].push_back(it2->chart1_id);
    }

    uint32_t join_id = 0;
    for (it2 = joins.begin(); it2 != joins.end(); ++it2){
      // combined neighbour count of joins' 2 charts should be at least 5
      // they will both contain each other (accounting for 2 neighbours) and require 3 more

      //merge the vectors for each chart in the join and count unique neighbours
      std::vector<uint32_t> combined_nbrs (neighbour_count[it2->chart1_id]);
      combined_nbrs.insert(combined_nbrs.end(), neighbour_count[it2->chart2_id].begin(), neighbour_count[it2->chart2_id].end());

      //find unique
      std::sort(combined_nbrs.begin(), combined_nbrs.end());
      uint32_t unique = 1;
      for (uint32_t i = 1; i < combined_nbrs.size(); i++){
        if (combined_nbrs[i] != combined_nbrs [i-1])
        {
          unique++;
        }
      }
      if (unique < 5)
      {
        to_erase.push_back(join_id);
      }
      join_id++;
    }
    //erase joins that would result in less than 3 corners
    to_erase.sort();
    num_erased = 0;
    for (auto id : to_erase) {
      std::list<JoinOperation>::iterator it2 = joins.begin();
      std::advance(it2, id - num_erased);
      joins.erase(it2);
      num_erased++;
    }
#endif

    chart_merges++;

  }

  // std::cout << "Printing Joins:\n";
  // int index = 0;
  // std::list<JoinOperation>::iterator it2;
  // for (it2 = joins.begin(); it2 != joins.end(); ++it2){
  //   std::cout << "Join " << ++index << ", cost " << it2->cost << std::endl;
  // }

  //reporting//testing

  std::cout << "front join cost: " << joins.front().cost << ", num joins: " << joins.size() << "chart threshold: " << chart_threshold << std::endl;

  std::cout << "--------------------\nCharts:\n----------------------\n";

  uint32_t total_faces = 0;
  uint32_t total_active_charts = 0;
  for (uint32_t i = 0; i < charts.size(); ++i)
  {
    if (charts[i].active)
    {
      uint32_t num_faces = charts[i].facets.size();
      total_faces += num_faces;
      total_active_charts++;
      // std::cout << "Chart " << i << " : " << num_faces << " faces" << std::endl;
    }
  }
  std::cout << "Total number of faces in charts = " << total_faces << std::endl;
  std::cout << "Initial charts = " << charts.size() << std::endl;
  std::cout << "Total number merges = " << chart_merges << std::endl;
  std::cout << "Total active charts = " << total_active_charts << std::endl;

  std::cout << "--------------------\nReport:\n----------------------\n";
  // std::cout << report.str();

  //populate LUT for face to chart mapping
  //count charts on the way to apply new chart ids
  uint32_t active_charts = 0;
  for (uint32_t id = 0; id < charts.size(); ++id) {
    auto& chart = charts[id];
    if (chart.active) {
      for (auto& f : chart.facets) {
        chart_id_map[f.id()] = active_charts;
      }
      active_charts++;
    }
  }

  return active_charts;

}

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

  std::string obj_filename = "dino.obj";
  if (Utils::cmdOptionExists(argv, argv+argc, "-f")) {
    obj_filename = std::string(Utils::getCmdOption(argv, argv + argc, "-f"));
  }
  else {
    std::cout << "Please provide an obj filename using -f <filename.obj>" << std::endl;
    std::cout << "Optional: -ch specifies chart threshold (=100)" << std::endl;
    std::cout << "Optional: -co specifies cost threshold (=double max)" << std::endl;

    std::cout << "Optional: -ef specifies error fit coefficient (=1)" << std::endl;
    std::cout << "Optional: -eo specifies error orientation coefficient (=1)" << std::endl;
    std::cout << "Optional: -es specifies error shape coefficient (=1)" << std::endl;
    return 1;
  }

  double cost_threshold = std::numeric_limits<double>::max();
  if (Utils::cmdOptionExists(argv, argv+argc, "-co")) {
    cost_threshold = atof(Utils::getCmdOption(argv, argv + argc, "-co"));
  }
  uint32_t chart_threshold = 100;
  if (Utils::cmdOptionExists(argv, argv+argc, "-ch")) {
    chart_threshold = atoi(Utils::getCmdOption(argv, argv + argc, "-ch"));
  }
  double e_fit_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-ef")) {
    e_fit_cf = atof(Utils::getCmdOption(argv, argv + argc, "-ef"));
  }
  double e_ori_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-eo")) {
    e_ori_cf = atof(Utils::getCmdOption(argv, argv + argc, "-eo"));
  }
  double e_shape_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-es")) {
    e_shape_cf = atof(Utils::getCmdOption(argv, argv + argc, "-es"));
  }
  CLUSTER_SETTINGS cluster_settings (e_fit_cf, e_ori_cf, e_shape_cf);

    //load OBJ into arrays
  std::vector<double> vertices;
  std::vector<int> tris;
  std::vector<double> t_coords;
  std::vector<int> tindices;
  Utils::load_obj( obj_filename, vertices, tris, t_coords, tindices);

  if (vertices.size() == 0 ) {
    std::cout << "didnt find any vertices" << std::endl;
    return 1;
  }
  std::cout << "Mesh loaded (" << vertices.size() / 3 << " vertices, " << tris.size() / 3 << " faces, " << t_coords.size() / 2 << " tex coords)" << std::endl;

  auto start_time = std::chrono::system_clock::now();

  // build a polyhedron from the loaded arrays
  Polyhedron polyMesh;
  bool check_vertices = true;
  polyhedron_builder<HalfedgeDS> builder( vertices, tris, t_coords, tindices, check_vertices );

  polyMesh.delegate( builder );

  if (polyMesh.is_valid(false)){
    std::cout << "mesh valid\n";
  }

  if (!CGAL::is_triangle_mesh(polyMesh)){
    std::cerr << "Input geometry is not triangulated." << std::endl;
    return EXIT_FAILURE;
  }
  else {
    std::cout << "mesh is triangulated\n";
  }

  uint32_t active_charts = create_charts(polyMesh, cost_threshold, chart_threshold, cluster_settings);

  std::string out_filename = "data/charts.obj";
  std::ofstream ofs( out_filename );
  OBJ_printer::print_polyhedron_wavefront_with_charts( ofs, polyMesh,chart_id_map, active_charts, SEPARATE_CHART_FILE);
  ofs.close();
  std::cout << "simplified mesh was written to " << out_filename << std::endl;

  //Logging
  auto time = std::chrono::system_clock::now();
  std::chrono::duration<double> diff = time - start_time;
  std::time_t now_c = std::chrono::system_clock::to_time_t(time);
  std::string log_path = "../../data/logs/chart_creation_log.txt";
  ofs.open (log_path, std::ofstream::out | std::ofstream::app);
  ofs << "\n-------------------------------------\n";

  ofs << "Executed at " << std::put_time(std::localtime(&now_c), "%F %T") << std::endl;
  ofs << "Ran for " << (int)diff.count() / 60 << " m "<< (int)diff.count() % 60 << " s" << std::endl;
  ofs << "Input file: " << obj_filename << "\nOutput file: " << out_filename << std::endl;
  ofs << "Vertices: " << vertices.size()/3 << " , faces: " << tris.size()/3 << std::endl;
  ofs << "Desired Charts: " << chart_threshold << ", active charts: " << active_charts << std::endl;
  ofs << "Cost threshold: " << cost_threshold << std::endl;
  ofs << "Cluster settings: e_fit: " << cluster_settings.e_fit_cf << ", e_ori: " << cluster_settings.e_ori_cf << ", e_shape" << cluster_settings.e_shape_cf << std::endl;

    ofs.close();
  std::cout << "Log written to " << log_path << std::endl;

  return EXIT_SUCCESS ;
}


// Copyright (c) 2018 yshurik
//
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// The use in another cyptocurrency project the code is licensed under
// Jelurida Public License (JPL). See https://www.jelurida.com/resources/jpl

#include "pegdata.h"
#include "utilstrencodings.h"

#include <map>
#include <set>
#include <cstdint>
#include <utility>
#include <algorithm>
#include <type_traits>

#include <boost/multiprecision/cpp_int.hpp>

#include <zconf.h>
#include <zlib.h>

using namespace std;
using namespace boost;

int64_t RatioPart(int64_t nValue,
                  int64_t nPartValue,
                  int64_t nTotalValue) {
    if (nPartValue == 0 || nTotalValue == 0)
        return 0;

    bool has_overflow = false;
    if (std::is_same<int64_t,long>()) {
        long m_test;
        has_overflow = __builtin_smull_overflow(nValue, nPartValue, &m_test);
    } else if (std::is_same<int64_t,long long>()) {
        long long m_test;
        has_overflow = __builtin_smulll_overflow(nValue, nPartValue, &m_test);
    } else {
        assert(0); // todo: compile error
    }

    if (has_overflow) {
        multiprecision::uint128_t v128(nValue);
        multiprecision::uint128_t part128(nPartValue);
        multiprecision::uint128_t f128 = (v128*part128)/nTotalValue;
        return f128.convert_to<int64_t>();
    }

    return (nValue*nPartValue)/nTotalValue;
}

CPegData::CPegData(std::string pegdata64)
{
    if (pegdata64.empty()) {
        peglevel = CPegLevel(0,0,0,0,0,0);
        return; // defaults, zeros
    }

    string pegdata = DecodeBase64(pegdata64);
    CDataStream finp(pegdata.data(),
                     pegdata.data() + pegdata.size(),
                     SER_NETWORK, CLIENT_VERSION);
    bool ok = Unpack(finp);
    if (!ok) {
        // try prev versions
        CDataStream finp(pegdata.data(),
                         pegdata.data() + pegdata.size(),
                         SER_DISK, CLIENT_VERSION);

        bool ok = Unpack2(finp);
        if (!ok) {
            // try prev versions
            CDataStream finp(pegdata.data(),
                              pegdata.data() + pegdata.size(),
                              SER_DISK, CLIENT_VERSION);
            ok = Unpack1(finp);

            if (!ok) {
                // peglevel to inicate invalid
                peglevel = CPegLevel();
            }
        }
    }
}

bool CPegData::IsValid() const
{
    if (!peglevel.IsValid()) return false;

    // match total
    if ((nReserve+nLiquid) != fractions.Total()) return false;

    // validate liquid/reserve match peglevel
    int nSupplyEffective = peglevel.nSupply+peglevel.nShift;
    bool fPartial = peglevel.nShiftLastPart >0 && peglevel.nShiftLastTotal >0;
    if (fPartial) {
        nSupplyEffective++;
        int64_t nLiquidWithoutPartial = fractions.High(nSupplyEffective);
        int64_t nReserveWithoutPartial = fractions.Low(nSupplyEffective-1);
        if (nLiquid < nLiquidWithoutPartial) return false;
        if (nReserve < nReserveWithoutPartial) return false;
    }
    else {
        int64_t nLiquidCalc = fractions.High(nSupplyEffective);
        int64_t nReserveCalc = fractions.Low(nSupplyEffective);
        if (nLiquid != nLiquidCalc) return false;
        if (nReserve != nReserveCalc) return false;
    }

    return true;
}

bool CPegData::Pack(CDataStream & fout) const {
    fout << nVersion;
    fractions.Pack(fout);
    peglevel.Pack(fout);
    fout << nReserve;
    fout << nLiquid;
    fout << nId;
    return true;
}

bool CPegData::Unpack(CDataStream & finp) {
    try {
        finp >> nVersion;
        if (!fractions.Unpack(finp)) return false;
        if (!peglevel.Unpack(finp)) return false;
        finp >> nReserve;
        finp >> nLiquid;
        finp >> nId;

        if (!IsValid())
            return false;
    }
    catch (std::exception &) {
        return false;
    }

    fractions = fractions.Std();
    return true;
}

std::string CPegData::ToString() const {
    CDataStream fout(SER_NETWORK, CLIENT_VERSION);
    Pack(fout);
    return EncodeBase64(fout.str());
}


/*! \file tdxscene.cpp
    \brief TDXSceneクラスの実装
    Copyright ©  2015 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "DXUT.h"
#include "DXUTmisc.h"
#include "myrandom/myrand.h"
#include "resource.h"
#include "TDXScene.h"
#include <mutex>                                                // for std::mutex
#include <boost/assert.hpp>                                     // for BOOST_ASSERT
#include <boost/cast.hpp>                                       // for boost::numeric_cast
#include <boost/math/special_functions/spherical_harmonic.hpp>  // for boost::math::spherical_harmonic
#include <boost/range/algorithm.hpp>                            // for boost::fill
#include <tbb/parallel_for.h>                                   // for tbb::parallel_for
#include <tbb/partitioner.h>                                    // for tbb::auto_partitioner

namespace tdxscene {
	float const TDXScene::MAGNIFICATION = 1.2f;

	TDXScene::TDXScene(std::shared_ptr<getdata::GetData> const & pgd) :
		Complete([this]{ return complete_.load(); }, nullptr),
		Pth([this]{ return std::cref(pth_); }, nullptr),
		Pgd(nullptr, [this](std::shared_ptr<getdata::GetData> const & val) {
			rmax_ = GetRmax(val);
			SetCamera();
			return pgd_ = val;
		}),
		PInputLayout([this]{ return std::cref(pInputLayout_); }, nullptr),
		Redraw(nullptr, [this](bool redraw){ return redraw_ = redraw; }),
		Thread_end(nullptr, [this](bool thread_end){
			thread_end_.store(thread_end);
			return thread_end; }),
		Vertexsize([this]{ return vertexsize_.load(); }, [this](std::vector<SimpleVertex2>::size_type size) {
				vertexsize_.store(size);
				return size; }),
		projectionVariable_(nullptr),
		pgd_(pgd),
		rmax_(GetRmax(pgd)),
		technique_(nullptr),
		vertices_(VERTEXSIZE_FIRST),
		viewVariable_(nullptr),
		worldVariable_(nullptr)
	{
	}

	HRESULT TDXScene::Init(ID3D10Device* pd3dDevice)
	{
		// Read the D3DX effect_ file
		auto dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
		// Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
		// Setting this flag improves the shader debugging experience, but still allows
		// the shaders to be optimized and to run exactly the way they will run in
		// the release configuration of this program.
		dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

		ID3D10Effect * effect_tmp;

		auto hr = D3DX10CreateEffectFromFile(
			L"SchracVisualize.fx",
			nullptr,
			nullptr,
			"fx_4_0",
			dwShaderFlags,
			0,
			pd3dDevice,
			nullptr,
			nullptr,
			&effect_tmp,
			nullptr,
			nullptr);

		effect_.reset(effect_tmp);

		if (FAILED(hr))
		{
			::MessageBox(nullptr,
				L"The FX file cannot be located.  Please run this executable from the directory that contains the FX file.",
				L"Error",
				MB_OK);
			if (!utility::v_return(hr)) {
				return S_FALSE;
			}
		}

		technique_ = effect_->GetTechniqueByName("Render2");
		worldVariable_ = effect_->GetVariableByName("World")->AsMatrix();
		viewVariable_ = effect_->GetVariableByName("View")->AsMatrix();
		projectionVariable_ = effect_->GetVariableByName("Projection")->AsMatrix();

		// Define the input layout
		D3D10_INPUT_ELEMENT_DESC layout[] =
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		};

		auto const numElements = sizeof(layout) / sizeof(layout[0]);

		// Create the input layout
		D3D10_PASS_DESC PassDesc;
		technique_->GetPassByIndex(0)->GetDesc(&PassDesc);
		ID3D10InputLayout * pVertexLayout;

		if (!utility::v_return(
			pd3dDevice->CreateInputLayout(
			layout,
			numElements,
			PassDesc.pIAInputSignature,
			PassDesc.IAInputSignatureSize,
			&pVertexLayout))) {
			return S_FALSE;
		}

		pInputLayout_.reset(pVertexLayout, utility::Safe_Release<ID3D10InputLayout>());

		// Set the input layout
		pd3dDevice->IASetInputLayout(pInputLayout_.get());

		bd_.Usage = D3D10_USAGE_DEFAULT;
		bd_.BindFlags = D3D10_BIND_VERTEX_BUFFER;
		bd_.CPUAccessFlags = 0;
		bd_.MiscFlags = 0;

		// Initialize the world matrices
		D3DXMatrixIdentity(&world_);

		// Initialize the view matrix
		SetCamera();

		return S_OK;
	}

	LRESULT TDXScene::MsgPrc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		return camera_.HandleMessages(hWnd, uMsg, wParam, lParam);
	}

	HRESULT TDXScene::OnFrameMove(double fTime, float fElapsedTime, void* pUserContext)
	{
		// Update the camera_'s position based on user input
		camera_.FrameMove(fElapsedTime);

		return S_OK;
	}

	HRESULT TDXScene::OnRender(ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext)
	{
		//
		// Clear the back buffer
		//
		auto const ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; // red, green, blue, alpha
		auto pRTV = DXUTGetD3D10RenderTargetView();
		pd3dDevice->ClearRenderTargetView(pRTV, ClearColor.begin());

		//
		// Clear the depth stencil
		//
		auto pDSV = DXUTGetD3D10DepthStencilView();
		pd3dDevice->ClearDepthStencilView(pDSV, D3D10_CLEAR_DEPTH, 1.0, 0);

		//
		// Update variables that change once per frame
		//
		auto matcamera = camera_.GetProjMatrix();
		projectionVariable_->SetMatrix(reinterpret_cast<float *>(const_cast<D3DXMATRIX *>(matcamera)));
		auto matview = camera_.GetViewMatrix();
		viewVariable_->SetMatrix(reinterpret_cast<float *>(const_cast<D3DXMATRIX *>(matview)));

		worldVariable_->SetMatrix(reinterpret_cast<float *>(&world_));

		//
		// Render the cube
		//
		D3D10_TECHNIQUE_DESC techDesc;
		technique_->GetDesc(&techDesc);
		for (auto p = 0U; p < techDesc.Passes; ++p)
		{
			technique_->GetPassByIndex(p)->Apply(0);
			pd3dDevice->Draw(vertexsize_, 0);
		}

		return S_OK;
	}

	HRESULT TDXScene::OnResize(ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
		const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext)
	{
		// Setup the projection parameters again
		auto const fAspect = static_cast<float>(pBackBufferSurfaceDesc->Width) / static_cast<float>(pBackBufferSurfaceDesc->Height);

		camera_.SetProjParams(D3DX_PI / 4, fAspect, 0.1f, 100.0f);
		camera_.SetWindow(pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height);
		camera_.SetButtonMasks(MOUSE_MIDDLE_BUTTON, MOUSE_WHEEL, MOUSE_LEFT_BUTTON);

		return S_OK;
	}

	HRESULT TDXScene::RedrawFunc(std::int32_t m, ID3D10Device * pd3dDevice, TDXScene::Re_Im_type reim)
	{
		if (redraw_) {
			if (vertices_.size() != vertexsize_) {
				vertices_.resize(vertexsize_);
			}

			pth_.reset(new std::thread([this, m, reim]{ ClearFillSimpleVertex2(m, reim); }), [this](std::thread * pth)
			{
				if (pth->joinable()) {
					thread_end_.store(true);
					pth->join();
				}

				utility::Safe_Delete<std::thread> sd;
				sd(pth);
			});
			redraw_ = false;
		}

		bd_.ByteWidth = sizeof(SimpleVertex2) * vertexsize_;

		static D3D10_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = vertices_.data();

		ID3D10Buffer * vertexBuffertmp;
		if (!utility::v_return(pd3dDevice->CreateBuffer(&bd_, &InitData, &vertexBuffertmp))) {
			return S_FALSE;
		}

		// Set vertex buffer
		static auto const stride = static_cast<UINT>(sizeof(SimpleVertex2));
		static auto const offset = 0U;
		pd3dDevice->IASetVertexBuffers(0, 1, &vertexBuffertmp, &stride, &offset);
		pVertexBuffer_.reset(vertexBuffertmp);

		return S_OK;
	}

	void TDXScene::ClearFillSimpleVertex2(std::int32_t m, TDXScene::Re_Im_type reim)
	{
		complete_.store(false);

		SimpleVertex2 sv2;
		sv2.Col = { 0.0f, 0.0f, 0.0f, 0.0f };
		sv2.Pos = { 0.0f, 0.0f, 0.0f };
		boost::fill(vertices_, sv2);

		tbb::parallel_for(
			0,
			boost::numeric_cast<std::int32_t>(vertexsize_.load()),
			1,
			[this, m, reim](std::int32_t i) { FillSimpleVertex2(m, reim, vertices_[i]); });

		complete_.store(true);
	}

	void TDXScene::FillSimpleVertex2(std::int32_t m, TDXScene::Re_Im_type reim, SimpleVertex2 & ver)
	{
		if (thread_end_) {
			return;
		}

		auto pp = 0.0, p = 0.0;
		auto sign = 0;
		double x, y, z;

		myrandom::MyRand mr(-rmax_, rmax_);
		myrandom::MyRand mr2(pgd_->Funcmin, pgd_->Funcmax);

		do {
			if (thread_end_) {
				return;
			}

			x = mr.myrand();
			y = mr.myrand();
			z = mr.myrand();

			auto const r = std::sqrt(x * x + y * y + z * z);
			if (r < pgd_->R_meshmin()) {
				continue;
			}

			switch (pgd_->Rho_wf_type_) {
			case getdata::GetData::Rho_Wf_type::RHO:
			{
				auto const phi = std::acos(x / std::sqrt(x * x + y * y));
                double v;
                if (m >= 0) {
                    v = boost::math::spherical_harmonic_r(pgd_->L, m, std::acos(z / r), phi);
                }
                else {
                    v = boost::math::spherical_harmonic_i(pgd_->L, m, std::acos(z / r), phi);
                }

                pp = ((*pgd_)(r) * v * v);
				p = mr2.myrand();
			}
			break;

			case getdata::GetData::Rho_Wf_type::WF:
			{
				auto const phi = std::acos(x / std::sqrt(x * x + y * y));
				double ylm = 0.0;
				switch (reim) {
				case TDXScene::Re_Im_type::REAL:
					ylm = boost::math::spherical_harmonic_r(pgd_->L, m, std::acos(z / r), phi);
					break;

				case TDXScene::Re_Im_type::IMAGINARY:
					ylm = boost::math::spherical_harmonic_i(pgd_->L, m, std::acos(z / r), phi);
					break;

				default:
					BOOST_ASSERT(!"何かがおかしい!");
					break;
				}

				pp = (*pgd_)(r) * ylm;
				p = mr2.myrand();
			}
			break;

			default:
				BOOST_ASSERT(!"何かがおかしい!");
				break;
			}

			switch (pgd_->Rho_wf_type_) {
			case getdata::GetData::Rho_Wf_type::RHO:
				sign = 1;
				break;

			case getdata::GetData::Rho_Wf_type::WF:
				sign = (pp > 0.0) - (pp < 0.0);
				break;

			default:
				BOOST_ASSERT(!"何かがおかしい!");
				break;
			}

			if (!m && pgd_->Rho_wf_type_ == getdata::GetData::Rho_Wf_type::WF && reim == TDXScene::Re_Im_type::IMAGINARY) {
				break;
			}

		} while (std::fabs(pp) < std::fabs(p));

		ver.Pos.x = static_cast<float>(x);
		ver.Pos.y = static_cast<float>(y);
		ver.Pos.z = static_cast<float>(z);

		ver.Col.r = sign > 0 ? 0.8f : 0.0f;
		ver.Col.b = 0.8f;
		ver.Col.g = sign < 0 ? 0.8f : 0.0f;
		ver.Col.a = 1.0f;
	}

	void TDXScene::SetCamera()
	{
		// Initialize the view matrix
		auto const pos = static_cast<float>(rmax_)* TDXScene::MAGNIFICATION;
		D3DXVECTOR3 Eye(0.0f, pos, -pos);
		D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
		camera_.SetViewParams(&Eye, &At);
	}

	double GetRmax(std::shared_ptr<getdata::GetData> const & pgd)
	{
		auto const n = static_cast<double>(pgd->N);
		return (2.3622 * n + 3.3340) * n + 1.3228;
	}
}


/*
// Copyright 2018 University of Liege
//
// 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.
//
// Authors:
// - Adrien Crovato
*/

//// Surface grid reading
// Read data from /IO/*.pts file which contain coordinates of points defining the corner of each panel
//
// I/O:
// - path: path to *.pts file
// - sGrid: temporary dynamic array to store panel vertices
// - bPan: body panel (structure)

#include <iostream>
#include <fstream>
#include <sstream>
#include <Eigen/Dense>
#include "read_sgrid.h"

#define NDIM 3

using namespace std;
using namespace Eigen;

void read_sgrid(string path, MatrixX3d &sGrid, Network &bPan){

    ifstream infile(path);
    string line;

    int i = 2;
    int a = 0;
    string section;
    bool secFLAG = 1;

    if(!infile.is_open())
    {
        cout << "File not found: " << path << endl;
        exit(EXIT_FAILURE);
    }
    else
        cout << "Found grid file: " << path << endl;

    getline(infile, line);
    cout << "Reading file... '" << line << "'" << endl;

    while (getline(infile, line))
    {

        if (secFLAG) {
            stringstream ss(line);
            ss >> section;
            secFLAG = 0;
        }

        else {

            if (section == "$size") {
                stringstream ss(line);
                ss >> bPan.nC >> bPan.nS;
                sGrid.resize(bPan.nC * bPan.nS, NDIM);
                secFLAG = 1;
            }

            else if (section == "$points") {
                stringstream ss(line);
                ss >> sGrid(a,0) >> sGrid(a,1) >> sGrid(a,2);
                a++;
            }

            else {
                cout << "Invalid section name: " << section << " at line " << i-1 << endl;
                exit(EXIT_FAILURE);
            }

        }
        i++;
    }

    // Set number of panels
    bPan.nC_ = bPan.nC - 1;
    bPan.nS_ = bPan.nS - 1;
    bPan.nP = bPan.nC_ * bPan.nS_;

    cout << "Done reading surface sGrid file!" << endl;
    cout << "Number of chordwise points: " << bPan.nC << endl;
    cout << "Number of spanwise points: " << bPan.nS << endl;
    cout << "Number of panels: " << bPan.nP << endl;
    #ifdef VERBOSE
        cout << "Surface points: " << sGrid.rows() << 'X' << sGrid.cols() << endl;
        for (int i = 0; i < bPan.nC*bPan.nS; ++i)
            cout << i << ' ' << sGrid(i,0) << ' ' << sGrid(i,1) << ' ' << sGrid(i,2) << endl;
    #endif
    cout << endl;
}