// Copyright (C) 2011-2013 Rhys Ulerich
// Copyright (C) ??? Martin Bauer
// Copyright (C) 2017 Henri Menke
//
// This code borrows heavily from code written by Rhys Ulerich and
// Martin Bauer.  They licensed it under the Mozilla Public License,
// v. 2.0 and the GNU General Public License (no version info),
// respectively.  I believe that I have made enough contributions and
// altered this code far enough from the originals that I can
// relicense it under the Boost Software License.
//
// 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)
#pragma once

#include <algorithm>
#include <cmath>
#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/math/constants/constants.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>

namespace matheval {

namespace detail {

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

/** @brief Sign function
 *
 * Missing function in the STL.  This calculates the mathematical sign
 * function.
 *
 * @f[
 *   \mathop{\mathrm{sgn}}(x) =
 *   \begin{cases}
 *      1 & x > 0 \\
 *      0 & x = 0 \\
 *     -1 & x < 0 \\
 *   \end{cases}
 * @f]
 *
 * @param[in] x number
 * @returns the sign of x
 */
template < typename T >
T sgn(T x) { return (T{0} < x) - (x < T{0}); }

// AST

template < typename real_t > struct unary_op;
template < typename real_t > struct binary_op;

struct nil {};

/** @brief Abstract Syntax Tree
 *
 * Stores the abstract syntax tree (AST) of the parsed mathematical
 * expression.
 */
template < typename real_t >
struct expr_ast
{
    using tree_t = boost::variant<
        nil // can't happen!
        , real_t
        , std::string
        , boost::recursive_wrapper<expr_ast<real_t>>
        , boost::recursive_wrapper<binary_op<real_t>>
        , boost::recursive_wrapper<unary_op<real_t>>
        >;
public:
    /** @brief AST storage
     *
     * The syntax tree can hold various types.  Numbers (`real_t`),
     * variables (`std::string`), the recursive tree itself
     * (`expr_ast`), binary operators (`binary_op`), and unary
     * operators (`unary_op`).
     */
    tree_t tree;

    /** @brief Default constructor
     *
     * Initializes the tree to a nil value to indicate inconsistent
     * state.
     */
    expr_ast() : tree(nil{}) {}

    /** @brief Copy constructor
     *
     * Deep copies the syntax tree.
     */
    template <typename Expr>
    expr_ast(Expr const &other) : tree(other) {}

    /** @brief Add a tree */
    expr_ast& operator+=(expr_ast const &rhs);
    /** @brief subtract a tree */
    expr_ast& operator-=(expr_ast const &rhs);
    /** @brief Multiply by a tree */
    expr_ast& operator*=(expr_ast const &rhs);
    /** @brief Divide by a tree */
    expr_ast& operator/=(expr_ast const &rhs);
};

/** @brief Store a unary operator and its argument tree */
template < typename real_t >
struct unary_op
{
    /** @brief Signature of a unary operator: op(x) */
    using op_t = std::function<real_t(real_t)>;

    /** @brief Save the operator and the argument tree */
    unary_op(op_t op, expr_ast<real_t> const &rhs)
        : op(op), rhs(rhs)
    {}

    /** @brief Stored operator */
    op_t op;
    /** @brief Stored argument tree */
    expr_ast<real_t> rhs;
};

/** @brief Store a binary operator and its argument trees */
template < typename real_t >
struct binary_op
{
    /** @brief Signature of a binary operator: op(x,y) */
    using op_t = std::function<real_t(real_t,real_t)>;

    /** @brief Save the operator and the argument trees */
    binary_op(op_t op, expr_ast<real_t> const &lhs, expr_ast<real_t> const &rhs)
        : op(op), lhs(lhs), rhs(rhs)
    {}

    /** @brief Stored operator */
    op_t op;
    /** @brief Stored argument tree of first argument */
    expr_ast<real_t> lhs;
    /** @brief Stored argument tree of second argument */
    expr_ast<real_t> rhs;
};

template < typename real_t >
expr_ast<real_t>& expr_ast<real_t>::operator+=(expr_ast<real_t> const &rhs)
{
    tree = binary_op<real_t>(std::plus<real_t>{}, tree, rhs);
    return *this;
}
template < typename real_t >
expr_ast<real_t>& expr_ast<real_t>::operator-=(expr_ast<real_t> const &rhs)
{
    tree = binary_op<real_t>(std::minus<real_t>{}, tree, rhs);
    return *this;
}
template < typename real_t >
expr_ast<real_t>& expr_ast<real_t>::operator*=(expr_ast<real_t> const &rhs)
{
    tree = binary_op<real_t>(std::multiplies<real_t>{}, tree, rhs);
    return *this;
}
template < typename real_t >
expr_ast<real_t>& expr_ast<real_t>::operator/=(expr_ast<real_t> const &rhs)
{
    tree = binary_op<real_t>(std::divides<real_t>{}, tree, rhs);
    return *this;
}

/** @brief Evaluate the Abstract Syntax Tree
 *
 * This visits all the variants of the AST and applies the stored
 * operators.
 */
template < typename real_t >
class eval_ast
{
public:
    /** @brief Necessary typedef for `boost::apply_visitor` */
    using result_type = real_t;

    /** @brief Type of the symbol table */
    using symbol_table_t = std::map<std::string, result_type>;

    /** @brief Constructor
     *
     * Saves the symbol table to apply variables.
     */
    eval_ast(symbol_table_t const &sym) : st(sym) {}

    /** @brief Empty nodes in the tree evaluate to 0 */
    result_type operator()(nil) const { return 0; }

    /** @brief Numbers evaluate to themselves */
    result_type operator()(result_type n)  const { return n; }

    /** @brief Variables evaluate to their value in the symbol table */
    result_type operator()(std::string const &c) const
    {
        auto it = st.find(c);
        if(it == st.end())
            throw std::invalid_argument("Unknown variable " + c);
        return it->second;
    }

    /** @brief Recursively evaluate the AST */
    result_type operator()(expr_ast<real_t> const& ast) const
    {
        return boost::apply_visitor(*this, ast.tree);
    }

    /** @brief Evaluate a binary operator and optionally recurse its operands */
    result_type operator()(binary_op<real_t> const& tree) const
    {
        return tree.op(
            boost::apply_visitor(*this, tree.lhs.tree),
            boost::apply_visitor(*this, tree.rhs.tree)
            );
    }

    /** @brief Evaluate a unary operator and optionally recurse its operand */
    result_type operator()(unary_op<real_t> const& tree) const
    {
        return tree.op(
            boost::apply_visitor(*this, tree.rhs.tree)
            );
    }

private:
    symbol_table_t st;
};

// Expressions

/** @brief Unary expression functor */
template < typename real_t >
struct unary_expr_ {
    /** @brief Make boost::phoenix::function happy */
    template < typename T > struct result { using type = T; };

    /** @brief Create a new AST containing the unary function */
    expr_ast<real_t> operator()(typename unary_op<real_t>::op_t op,
                                expr_ast<real_t> const &rhs) const {
        return expr_ast<real_t>(unary_op<real_t>(op, rhs));
    }
};

/** @brief Binary expression functor */
template < typename real_t >
struct binary_expr_ {
    /** @brief Make boost::phoenix::function happy */
    template < typename T > struct result { using type = T; };

    /** @brief Create a new AST containing the binary function */
    expr_ast<real_t> operator()(typename binary_op<real_t>::op_t op,
                                expr_ast<real_t> const &lhs,
                                expr_ast<real_t> const &rhs) const {
        return expr_ast<real_t>(binary_op<real_t>(op, lhs, rhs));
    }
};

// Grammar

/** @brief Expression Grammar */
template < typename real_t, typename Iterator >
struct grammar
    : qi::grammar<
            Iterator, expr_ast<real_t>(), ascii::space_type
        >
{
private:
    qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> expression;
    qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> term;
    qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> factor;
    qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> primary;
    qi::rule<Iterator, std::string()> variable;
public:
    /** @brief symbol table for constants like "pi" */
    struct constant_
        : boost::spirit::qi::symbols<
                typename std::iterator_traits<Iterator>::value_type,
                real_t
            >
    {
        constant_()
        {
            this->add
                ("e"      , boost::math::constants::e<real_t>()   )
                ("epsilon", std::numeric_limits<real_t>::epsilon())
                ("pi"     , boost::math::constants::pi<real_t>()  )
            ;
        }
    } constant;

    /** @brief symbol table for unary functions like "abs" */
    struct ufunc_
        : boost::spirit::qi::symbols<
                typename std::iterator_traits<Iterator>::value_type,
                typename unary_op<real_t>::op_t
            >
    {
        ufunc_()
        {
            this->add
                ("abs"   , static_cast<real_t(*)(real_t)>(&std::abs   ))
                ("acos"  , static_cast<real_t(*)(real_t)>(&std::acos  ))
                ("acosh" , static_cast<real_t(*)(real_t)>(&std::acosh ))
                ("asin"  , static_cast<real_t(*)(real_t)>(&std::asin  ))
                ("asinh" , static_cast<real_t(*)(real_t)>(&std::asinh ))
                ("atan"  , static_cast<real_t(*)(real_t)>(&std::atan  ))
                ("atanh" , static_cast<real_t(*)(real_t)>(&std::atanh ))
                ("cbrt"  , static_cast<real_t(*)(real_t)>(&std::cbrt  ))
                ("ceil"  , static_cast<real_t(*)(real_t)>(&std::ceil  ))
                ("cos"   , static_cast<real_t(*)(real_t)>(&std::cos   ))
                ("cosh"  , static_cast<real_t(*)(real_t)>(&std::cosh  ))
                ("erf"   , static_cast<real_t(*)(real_t)>(&std::erf   ))
                ("erfc"  , static_cast<real_t(*)(real_t)>(&std::erfc  ))
                ("exp"   , static_cast<real_t(*)(real_t)>(&std::exp   ))
                ("exp2"  , static_cast<real_t(*)(real_t)>(&std::exp2  ))
                ("floor" , static_cast<real_t(*)(real_t)>(&std::floor ))
                ("log"   , static_cast<real_t(*)(real_t)>(&std::log   ))
                ("log2"  , static_cast<real_t(*)(real_t)>(&std::log2  ))
                ("log10" , static_cast<real_t(*)(real_t)>(&std::log10 ))
                ("round" , static_cast<real_t(*)(real_t)>(&std::round ))
                ("sgn"   , static_cast<real_t(*)(real_t)>(&sgn        ))
                ("sin"   , static_cast<real_t(*)(real_t)>(&std::sin   ))
                ("sinh"  , static_cast<real_t(*)(real_t)>(&std::sinh  ))
                ("sqrt"  , static_cast<real_t(*)(real_t)>(&std::sqrt  ))
                ("tan"   , static_cast<real_t(*)(real_t)>(&std::tan   ))
                ("tanh"  , static_cast<real_t(*)(real_t)>(&std::tanh  ))
                ("tgamma", static_cast<real_t(*)(real_t)>(&std::tgamma))
            ;
        }
    } ufunc;

    /** @brief symbol table for binary functions like "pow" */
    struct bfunc_
        : boost::spirit::qi::symbols<
                typename std::iterator_traits<Iterator>::value_type,
                typename binary_op<real_t>::op_t
            >
    {
        bfunc_()
        {
            this->add
                ("atan2", static_cast<real_t(*)(real_t,real_t)>(&std::atan2))
                ("max"  , static_cast<real_t(*)(real_t,real_t)>(&std::fmax ))
                ("min"  , static_cast<real_t(*)(real_t,real_t)>(&std::fmin ))
                ("pow"  , static_cast<real_t(*)(real_t,real_t)>(&std::pow  ))
            ;
        }
    } bfunc;

    /** @brief Constructor builds the grammar */
    grammar() : grammar::base_type(expression)
    {
        using boost::spirit::qi::real_parser;
        using boost::spirit::qi::real_policies;
        real_parser<real_t,real_policies<real_t>> real;

        using boost::spirit::lexeme;
        using boost::spirit::qi::_1;
        using boost::spirit::qi::_2;
        using boost::spirit::qi::_3;
        using boost::spirit::qi::_val;
        using boost::spirit::qi::alpha;
        using boost::spirit::qi::alnum;
        using boost::spirit::qi::raw;

        boost::phoenix::function<unary_expr_<real_t>> unary_expr;
        boost::phoenix::function<binary_expr_<real_t>> binary_expr;

        auto fmod = static_cast<real_t(*)(real_t,real_t)>(&std::fmod);
        auto pow = static_cast<real_t(*)(real_t,real_t)>(&std::pow);

        expression =
            term                   [_val =  _1]
            >> *(  ('+' >> term    [_val += _1])
                |  ('-' >> term    [_val -= _1])
                )
            ;

        term =
            factor                 [_val =  _1]
            >> *(  ('*' >> factor  [_val *= _1])
                |  ('/' >> factor  [_val /= _1])
                |  ('%' >> factor  [_val = binary_expr(fmod, _val, _1)])
                )
            ;

        factor =
            primary                [_val =  _1]
            >> *(  ("**" >> factor [_val = binary_expr(pow, _val, _1)])
                )
            ;

        variable =
            raw[lexeme[alpha >> *(alnum | '_')]];

        primary =
            real                   [_val =  _1]
            |   '(' >> expression  [_val =  _1] >> ')'
            |   ('-' >> primary    [_val = unary_expr(std::negate<real_t>{}, _1)])
            |   ('+' >> primary    [_val =  _1])
            |   (ufunc >> '(' >> expression >> ')')
                                   [_val = unary_expr(_1, _2)]
            |   (bfunc >> '(' >> expression >> ',' >> expression >> ')')
                                   [_val = binary_expr(_1, _2, _3)]
            |   constant           [_val =  _1]
            |   variable           [_val =  _1]
            ;
    }
};

} // namespace detail

/** @brief Class interface
 *
 * This class hides the grammar, AST, and AST traversal behind some
 * member functions.
 *
 * @tparam real_t datatype of the result
 */
template < typename real_t >
class Parser
{
    detail::expr_ast<real_t> ast;
public:
    /** @brief Parse an expression
     *
     * This function builds the grammar and parses the iterator into
     * an AST.
     *
     * @param[in] first iterator to the start of the input sequence
     * @param[in] last  iterator to the end of the input sequence
     */
    template < typename Iterator >
    void parse(Iterator first, Iterator last)
    {
        static detail::grammar<real_t,Iterator> const g;

        ast = detail::expr_ast<real_t>{}; // Drop old AST

        bool r = boost::spirit::qi::phrase_parse(
            first, last, g,
            boost::spirit::ascii::space, ast);

        if (!r || first != last)
        {
            std::string rest(first, last);
            throw std::runtime_error("Parsing failed at " + rest);
        }
    }

    /** @overload parse(Iterator first, Iterator last) */
    void parse(std::string const &str)
    {
        parse(str.begin(), str.end());
    }

    /** @brief Evaluate the AST with a given symbol table
     *
     * @param[in] st the symbol table for variables
     */
    real_t evaluate(typename detail::eval_ast<real_t>::symbol_table_t const &st)
    {
        detail::eval_ast<real_t> solver(st);
        return solver(ast);
    }
};

/** @brief Convenience function
 *
 * This function builds the grammar, parses the iterator to an AST,
 * evaluates it, and returns the result.
 *
 * @param[in] first iterator to the start of the input sequence
 * @param[in] last  iterator to the end of the input sequence
 * @param[in] st    the symbol table for variables
 */
template < typename real_t, typename Iterator >
real_t parse(Iterator first, Iterator last,
             typename detail::eval_ast<real_t>::symbol_table_t const &st)
{
    Parser<real_t> parser;
    parser.parse(first, last);
    return parser.evaluate(st);
}

/** @overload parse(Iterator first, Iterator last, typename detail::eval_ast<real_t>::symbol_table_t const &st) */
template < typename real_t >
real_t parse(std::string const &str,
             typename detail::eval_ast<real_t>::symbol_table_t const &st)
{
    return parse<real_t>(str.begin(), str.end(), st);
}

} // namespace expression


//=======================================================================
// Copyright (c) 2013 Piotr Smulewicz
//
// 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)
//=======================================================================
/**
 * @file multiway_cut_example.cpp
 * @brief
 * @author Piotr Smulewicz
 * @version 1.0
 * @date 2013-11-13
 */

    //! [Multiway Cut Example]
#include "paal/multiway_cut/multiway_cut.hpp"

#include <boost/graph/adjacency_list.hpp>

int main() {
    // sample data
    std::vector<std::pair<int,int>> edges_p{{0,3},{1,3},
                                           {0,4},{2,4},
                                           {1,5},{2,5},
                                           {3,6},{4,6},
                                           {3,7},{5,7},
                                           {4,8},{5,8},
                                           {6,7},{6,8},{7,8}
    };
    const int vertices_num = 9;
    std::vector<int> cost_edges{100,100,100,100,100,100,10,10,10,10,10,10,1,1,1};

    std::vector<int> terminals = { 0, 1, 2 };
    boost::adjacency_list<
        boost::vecS, boost::vecS, boost::undirectedS,
        boost::property<boost::vertex_index_t, int,
                        boost::property<boost::vertex_color_t, int>>,
                    boost::property<boost::edge_weight_t, int>
                    > graph(edges_p.begin(), edges_p.end(), cost_edges.begin(), vertices_num);

    for (std::size_t i = 1; i <= terminals.size(); ++i) {
        put(boost::vertex_color, graph, terminals[i - 1], i);
    }

    //solve
    std::vector<std::pair<int,int>> vertices_parts;
    auto cost_cut = paal::multiway_cut(graph, back_inserter(vertices_parts));

    //print result
    std::cout << "cost cut: " << cost_cut << std::endl;
    std::cout << "vertices (part)" << std::endl;
    for(auto i: vertices_parts) {
        std::cout << "  " << i.first << "      ( " << i.second << " )" << std::endl;
    }
    paal::lp::glp::free_env();
}
    //! [Multiway Cut Example]


#include "Planet.h"
#include "helper/benchmark_helper.h"
#include <cmath>
#include <vector>
#include <chrono>
#include <string>
#include <memory>
#ifdef GPU_SUPPORT
#include <amp.h>
#include <amp_math.h>
#endif
#include <algorithm>
#include <thread>
#include <boost/math/special_functions/sign.hpp>

using std::vector;
using std::chrono::steady_clock;
using std::chrono::microseconds;
using std::chrono::duration_cast;
using std::string;
using std::to_string;
using std::unique_ptr;
using std::make_unique;
using std::min;
using std::max;
using std::thread;

using namespace GeneticSimulation;

// default constructor
GeneticSimulation::Planet::Planet() : initialized(false), timesteps(0) {}

// precompute temperatures
void GeneticSimulation::Planet::precompute_temperatures(const Config& config, bool benchmark)
{
	// set lookup table to correct size
	temperatures.resize(config.area_height * config.orbital_period);
	timesteps = config.orbital_period;

#ifdef GPU_SUPPORT
	// get whether to use GPU
	const bool use_cpu = !config.precompute_temperatures_gpu;
#else
	constexpr bool use_cpu = true;
#endif

	// precompute or benchmark on selected device
	if (use_cpu) {
		// determine number of threads to use
		auto num_threads = config.precompute_temperatures_cpu_threads == 0 ?
			thread::hardware_concurrency() :
			config.precompute_temperatures_cpu_threads;
		// benchmark or precompute once
		benchmark ? benchmark_temperature_computation_cpu(num_threads, config) :
			precompute_temperatures_cpu(num_threads, config);
	}
#ifdef GPU_SUPPORT
	else {
		// benchmark or precompute once
		benchmark ? benchmark_temperature_computation_gpu(config) :
			precompute_temperatures_gpu(config);
	}
#endif

	// record initialization
	initialized = true;
}

// get temperature from lookup table
float GeneticSimulation::Planet::get_temperature(unsigned int y, unsigned int t) const
{
	// return -1 if temperatures have not been computed, otherwise return precomputed temperature
	return initialized ? temperatures[y * timesteps + (t % timesteps)] : -1.f;
}

// precompute temperatures using the CPU
void GeneticSimulation::Planet::precompute_temperatures_cpu(unsigned int worker_threads, const Config& config)
{
	// calculate number of timesteps per thread
	unsigned int timesteps_per_thread = timesteps / worker_threads + 1;
	// create vector for thread objects
	vector<unique_ptr<thread>> threads;
	// start threads
	for (unsigned int i = 0; i < worker_threads; i++) {
		threads.push_back(make_unique<thread>(
			[&, timesteps_per_thread, i] {
				precompute_temperatures_for_timestep_range_cpu(i * timesteps_per_thread,
					(i + 1) * timesteps_per_thread, config);
			}
		));
	}
	// join threads
	for (auto& t_ptr : threads) {
		t_ptr->join();
	}
}

// precompute temperatures on the CPU for the given timestep range
void GeneticSimulation::Planet::precompute_temperatures_for_timestep_range_cpu(unsigned int start_t, unsigned int end_t, const Config& config)
{
	// cap end_t
	end_t = min(end_t, config.orbital_period);

	// initialize vector for storing intermediate equatorial temperature results
	vector<double> equatorial_black_body_temperatures(end_t - start_t);

	// pi
	const double pi = 3.14159265358979323846;

	// reused loop variables
	double angle, pos_x, pos_y, squared_dist, black_body_temperature;

	// calculate equatorial black body temperatures for each timestep (angle in orbit)
	for (unsigned int t = start_t; t < end_t; t++)
	{
		// calculate orbital angle corresponding to timestep
		angle = (static_cast<double>(t) / static_cast<double>(config.orbital_period)) * 2 * pi;

		// calculate the x and y coordinates of the planet at this angle in the orbital ellipse
		pos_x = (config.orbit_radius_x * cos(angle) * cos(config.orbit_rotation)) -
			(config.orbit_radius_y * sin(angle) * sin(config.orbit_rotation)) +
			config.orbit_center_offset_x;
		pos_y = (config.orbit_radius_x * cos(angle) * sin(config.orbit_rotation)) +
			(config.orbit_radius_y * sin(angle) * cos(config.orbit_rotation)) +
			config.orbit_center_offset_y;
		// calculate squared distance from star (0, 0) based on these coordinates
		squared_dist = pos_x * pos_x + pos_y * pos_y;

		// calculate equivalent black body temperature based on this squared distance
		black_body_temperature = pow((config.star_luminosity * (1 - config.albedo)) /
			(16 * pi * squared_dist * 5.670373e-8), 0.25);

		// calculate an approximated equatorial temperature from the average black body temperature
		equatorial_black_body_temperatures[t - start_t] = black_body_temperature / cos(pi / 6.0);
	}

	// reused loop variables
	double latitude, angle_from_vernal_equinox, effective_axial_tilt, effective_latitude,
		height_to_latitude, effective_tilt_plane_dist, width_at_latitude, plane_dist_radius_ratio,
		extra_logitude, daylight_proportion, radiation_strength, base_temperature, moderated_temperature;

	// calculate final temperatures based on timestep (angle in orbit) and y position (latitude)
	for (unsigned int y = 0; y < config.area_height; y++)
	{
		// calculate latitude corresponding to y coordinate
		latitude = -(((static_cast<double>(y) / static_cast<double>(config.area_height - 1))
			* (90.f - -90.f)) - 90.f);

		for (unsigned int t = start_t; t < end_t; t++)
		{
			// calculate orbital angle corresponding to timestep
			angle = (static_cast<double>(t) / static_cast<double>(config.orbital_period)) * 2 * pi;

			// calculate effective axial tilt
			angle_from_vernal_equinox = angle + config.orbit_rotation;
			effective_axial_tilt = sin(angle_from_vernal_equinox) * config.axial_tilt;

			// calculate effective latitude based on effective axial tilt
			effective_latitude = latitude - effective_axial_tilt;

			// calculate the vertical height to the current latitude
			height_to_latitude = sin((latitude / 360.0) * 2 * pi) * config.radius;
			// calculate distance between axially tilted plane and plane
			// dividing day and night, travelling along latitude
			effective_tilt_plane_dist = tan((effective_axial_tilt / 360.0) * 2 * pi) * height_to_latitude;
			// calculate the width of the planet at the current latitude
			width_at_latitude = max(0., cos((latitude / 360.0) * 2 * pi) * config.radius);
			// calculate a safe ratio of the plane distance to the width at latitude
			plane_dist_radius_ratio = width_at_latitude == 0 ?
				boost::math::sign(effective_tilt_plane_dist) :
				effective_tilt_plane_dist / width_at_latitude;
			// calculate the extra longitude in or out of daylight
			extra_logitude = asin(max(-1.0, (min(1.0, plane_dist_radius_ratio))));
			// calculate the proportion of daylight hours at current latitude and effective tilt
			daylight_proportion = (pi + 2.0 * extra_logitude) / (2 * pi);

			// calculate solar radiation strength at current effective latitude
			radiation_strength = max(0., cos((effective_latitude / 360) * 2 * pi));

			// calculate base temperature
			base_temperature = equatorial_black_body_temperatures[t - start_t]
				* radiation_strength * (daylight_proportion * 2);

			// calculate moderated temperature to account for convection etc.
			moderated_temperature = ((base_temperature
				- (equatorial_black_body_temperatures[t - start_t] * config.temperature_moderation_bias))
				/ config.temperature_moderation_factor) + (equatorial_black_body_temperatures[t - start_t]
					* config.temperature_moderation_bias);

			// calculate final temperature which accounts for greenhouse effect
			temperatures[y * config.orbital_period + t] = moderated_temperature
				* pow((1 + 0.75 * config.atmosphere_optical_thickness), 0.25);
		}
	}
}

#ifdef GPU_SUPPORT
// precompute temperatures on the GPU
void GeneticSimulation::Planet::precompute_temperatures_gpu(const Config& config)
{
	// use concurrency namespace for C++ AMP
	using namespace concurrency;

	// pi
	const float pi = 3.14159f;

	// initialize vector for storing intermediate equatorial temperature results
	vector<float> equatorial_black_body_temperatures(config.orbital_period);
	// initialize array view for this vector
	array_view<float, 1> equatorial_black_body_temperatures_av(config.orbital_period,
		equatorial_black_body_temperatures);
	// do not transfer 0s to GPU
	equatorial_black_body_temperatures_av.discard_data();

	// create array view for temperatures lookup table
	array_view<float, 2> temperatures_av(config.area_height, config.orbital_period, temperatures);
	// do not transfer 0s to GPU
	temperatures_av.discard_data();

	// save config values in local variables so AMP can use them
	const unsigned int area_height = config.area_height;
	const float latitude_range = config.latitude_range;
	const unsigned int orbital_period = config.orbital_period;
	const float orbit_center_offset_x = config.orbit_center_offset_x;
	const float orbit_center_offset_y = config.orbit_center_offset_y;
	const float orbit_radius_x = config.orbit_radius_x;
	const float orbit_radius_y = config.orbit_radius_y;
	const float orbit_rotation = config.orbit_rotation;
	const float star_luminosity = config.star_luminosity;
	const float albedo = config.albedo;
	const float axial_tilt = config.axial_tilt;
	const float radius = config.radius;
	const float atmosphere_optical_thickness = config.atmosphere_optical_thickness;
	const float temperature_moderation_factor = config.temperature_moderation_factor;
	const float temperature_moderation_bias = config.temperature_moderation_bias;

	// kernel to calculate equatorial black body temperatures for each time step
	parallel_for_each(
		equatorial_black_body_temperatures_av.extent,
		[=](index<1> idx) restrict(amp) {
			// calculate orbital angle corresponding to timestep
			float angle = (static_cast<float>(idx[0]) / static_cast<float>(orbital_period)) * 2 * pi;

			// calculate the x and y coordinates of the planet at this angle in the orbital ellipse
			float pos_x = (orbit_radius_x * fast_math::cos(angle) * fast_math::cos(orbit_rotation)) -
				(orbit_radius_y * fast_math::sin(angle) * fast_math::sin(orbit_rotation)) +
				orbit_center_offset_x;
			float pos_y = (orbit_radius_x * fast_math::cos(angle) * fast_math::sin(orbit_rotation)) +
				(orbit_radius_y * fast_math::sin(angle) * fast_math::cos(orbit_rotation)) +
				orbit_center_offset_y;
			// calculate squared distance from star (0, 0) based on these coordinates
			float squared_dist = pos_x * pos_x + pos_y * pos_y;

			// calculate equivalent black body temperature based on this squared distance
			float black_body_temperature = fast_math::pow((star_luminosity * (1 - albedo)) /
				(16 * pi * squared_dist * 5.670373e-8), 0.25);

			// calculate an approximated equatorial temperature from the average black body temperature
			equatorial_black_body_temperatures_av[idx] = black_body_temperature / fast_math::cos(pi / 6.0);
		}
	);

	// kernel to calculate final temperatures for each time step and latitude
	parallel_for_each(
		temperatures_av.extent,
		[=](index<2> idx) restrict(amp)
		{
			// calculate latitude corresponding to y coordinate
			float latitude = -(((static_cast<float>(idx[0]) / static_cast<float>(area_height - 1))
				* (90.f - -90.f)) - 90.f);

			// calculate orbital angle corresponding to timestep
			float angle = (static_cast<float>(idx[1]) / static_cast<float>(orbital_period)) * 2 * pi;

			// calculate effective axial tilt
			float angle_from_vernal_equinox = angle + orbit_rotation;
			float effective_axial_tilt = fast_math::sin(angle_from_vernal_equinox) * axial_tilt;

			// calculate effective latitude based on effective axial tilt
			float effective_latitude = latitude - effective_axial_tilt;

			// calculate the vertical height to the current latitude
			float height_to_latitude = fast_math::sin((latitude / 360.0) * 2 * pi) * radius;
			// calculate distance between axially tilted plane and plane
			// dividing day and night, travelling along latitude
			float effective_tilt_plane_dist = fast_math::tan((effective_axial_tilt / 360.0) * 2 * pi) * height_to_latitude;
			// calculate the width of the planet at the current latitude
			float width_at_latitude = max(0, fast_math::cos((latitude / 360.0) * 2 * pi) * radius);
			// calculate a safe ratio of the plane distance to the width at latitude
			float plane_dist_radius_ratio = width_at_latitude == 0 ?
				(0 < effective_tilt_plane_dist) - (effective_tilt_plane_dist < 0) :
				effective_tilt_plane_dist / width_at_latitude;
			// calculate the extra longitude in or out of daylight
			float extra_logitude = fast_math::asin(max(-1.0, (min(1.0, plane_dist_radius_ratio))));
			// calculate the proportion of daylight hours at current latitude and effective tilt
			float daylight_proportion = (pi + 2.0 * extra_logitude) / (2 * pi);

			// calculate solar radiation strength at current effective latitude
			float radiation_strength = max(0, fast_math::cos((effective_latitude / 360) * 2 * pi));

			// save equatorial black body temperature
			float equatorial_black_body_temperature = equatorial_black_body_temperatures_av[idx[1]];

			// calculate base temperature
			float base_temperature = equatorial_black_body_temperature
				* radiation_strength * (daylight_proportion * 2);

			// calculate moderated temperature to account for convection etc.
			float moderated_temperature = ((base_temperature
				- (equatorial_black_body_temperature * temperature_moderation_bias))
				/ temperature_moderation_factor) + (equatorial_black_body_temperature
					* temperature_moderation_bias);

			// calculate final temperature which accounts for greenhouse effect
			temperatures_av[idx] = moderated_temperature
				* fast_math::pow((1 + 0.75 * atmosphere_optical_thickness), 0.25);
		}
	);

	// transfer temperature data from GPU
	temperatures_av.synchronize();
}
#endif

// benchmark precomputation on the CPU
void GeneticSimulation::Planet::benchmark_temperature_computation_cpu(unsigned int worker_threads, const Config& config)
{
	// time points for benchmarking
	steady_clock::time_point start, end;

	// allocate space to store results
	vector<unsigned long long> times(config.planet_benchmark_samples);

	// precompute temperatures and record results
	for (unsigned int i = 0; i < config.planet_benchmark_samples; i++) {
		start = steady_clock::now();
		precompute_temperatures_cpu(worker_threads, config);
		end = steady_clock::now();
		times[i] = duration_cast<microseconds>(end - start).count();
	}

	// output results to file
	string filename = "planet_benchmark_cpu_" + to_string(worker_threads) + "_threads.csv";
	string header = "time_microseconds_" + to_string(worker_threads) + " _threads";
	write_benchmark_results(times, header, filename, config.results_path);
}

#ifdef GPU_SUPPORT
// benchmark precomputation on the GPU
void GeneticSimulation::Planet::benchmark_temperature_computation_gpu(const Config& config)
{
	// time points for benchmarking
	steady_clock::time_point start, end;

	// allocate space to store results
	vector<unsigned long long> times(config.planet_benchmark_samples);

	// precompute temperatures and record results
	for (unsigned int i = 0; i < config.planet_benchmark_samples; i++) {
		start = steady_clock::now();
		precompute_temperatures_gpu(config);
		end = steady_clock::now();
		times[i] = duration_cast<microseconds>(end - start).count();
	}

	// output results to file
	string filename = "planet_benchmark_gpu.csv";
	string header = "time_microseconds_gpu";
	write_benchmark_results(times, header, filename, config.results_path);
}
#endif


#include <fmt/core.h>
#include <string_view>
#include <chrono>

#include <Eigen/Core>
#include <Eigen/Dense>

#include <autodiff/forward/real.hpp>
#include <autodiff/forward/real/eigen.hpp>

#include "types.hh"
#include "cvode_wrapper.hh"

using namespace types;

struct System
{
    template<class T>
    inline auto f(Eigen::MatrixBase<T>& y, double t)
    {
        using mt = T::PlainObject;
        mt ydot(y.rows());

        ydot(0) = -0.04*y(0) + 1.0E4*y(1)*y(2);
        ydot(2) = 3.0E7*y(1)*y(1);
        ydot(1) = -ydot(0) - ydot(2);

        return ydot;
    }

    template<class T>
    inline auto J(Eigen::MatrixBase<T>& y, double t)
    {
        auto _f = [this](auto&& ...args){ return f(std::forward<decltype(args)>(args)...); };
        dvector_t dydt(y.size());
        dvector_t yy = y;
        matrix_t J = autodiff::jacobian(_f, autodiff::wrt(yy), autodiff::at(yy,t), dydt);
        return J;
    }
};

int main()
{

    vector_t y0(3);
    y0(0) = 1.0; y0(1) = 0.0; y0(2) = 0.0;

    auto stepper = cvode_wrapper::cvode_stepper<System>(cvode_wrapper::cv_options{});
    stepper.initialize(y0);

    stepper.letsgo();
    return 0;
}


#pragma once
#include <Eigen/Core>
#include <cppad/cppad.hpp>
#include <limits>

namespace Eigen {

template <class Base>
struct NumTraits<CppAD::AD<Base>> {
    // type that corresponds to the real part of an AD<Base> value
    typedef CppAD::AD<Base> Real;
    // type for AD<Base> operations that result in non-integer values
    typedef CppAD::AD<Base> NonInteger;
    //  type to use for numeric literals such as "2" or "0.5".
    typedef CppAD::AD<Base> Literal;
    // type for nested value inside an AD<Base> expression tree
    typedef CppAD::AD<Base> Nested;

    enum {
        // does not support complex Base types
        IsComplex             = 0 ,
        // does not support integer Base types
        IsInteger             = 0 ,
        // only support signed Base types
        IsSigned              = 1 ,
        // must initialize an AD<Base> object
        RequireInitialization = 1 ,
        // computational cost of the corresponding operations
        ReadCost              = 1 ,
        AddCost               = 2 ,
        MulCost               = 2
    };

    // machine epsilon with type of real part of x
    // (use assumption that Base is not complex)
    static CppAD::AD<Base> epsilon(void)
    {
        return CppAD::numeric_limits<CppAD::AD<Base>>::epsilon();
    }

    // relaxed version of machine epsilon for comparison of different
    // operations that should result in the same value
    static CppAD::AD<Base> dummy_precision(void)
    {
        return 100. * CppAD::numeric_limits<CppAD::AD<Base>>::epsilon();
    }

    // minimum normalized positive value
    static CppAD::AD<Base> lowest(void)
    {
        return CppAD::numeric_limits< CppAD::AD<Base> >::min();
    }

    // maximum finite value
    static CppAD::AD<Base> highest(void)
    {
        return CppAD::numeric_limits< CppAD::AD<Base> >::max();
    }

    // number of decimal digits that can be represented without change.
    static int digits10(void)
    {
        return CppAD::numeric_limits< CppAD::AD<Base> >::digits10;
    }
};

}

namespace CppAD {

// functions that return references
template <class Base>
const AD<Base>& conj(const AD<Base>& x) { return x; }
template <class Base>
const AD<Base>& real(const AD<Base>& x) { return x; }

// functions that return values (note abs is defined by cppad.hpp)
template <class Base> AD<Base>
imag(const AD<Base>& x) { return CppAD::AD<Base>(0.); }
template <class Base> AD<Base>
abs2(const AD<Base>& x) { return x * x; }

}

template <typename Type>
Eigen::Matrix<Type, 3, 3> exp3x3AD(const Eigen::Matrix<Type, 3, 1>& ax)
{
    Eigen::Matrix<Type, 3, 3> out;
    out.setZero();
    Type angle = CppAD::sqrt(ax.dot(ax));
    if (CppAD::abs(angle) < std::numeric_limits<double>::epsilon()) {
        out(0, 0) = Type(1);
        out(1, 1) = Type(1);
        out(2, 2) = Type(1);
    } else {
        Type x = ax(0) / angle;
        Type y = ax(1) / angle;
        Type z = ax(2) / angle;
        Type sa = CppAD::sin(angle);
        Type ca = CppAD::cos(angle);
        Type t = 1 - ca;

        out(0, 0) = ca + t * x * x;
        out(1, 0) = t * y * x + sa * z;
        out(2, 0) = t * z * x - sa * y;
        out(0, 1) = t * x * y - sa * z;
        out(1, 1) = ca + t * y * y;
        out(2, 1) = t * z * y + sa * x;
        out(0, 2) = t * x * z + sa * y;
        out(1, 2) = t * y * z - sa * x;
        out(2, 2) = ca + t * z * z;
    }

    return out;
}


// boost1.67-1.67.0/libs/graph/example/r_c_shortest_paths_example.cpp

// Copyright Michael Drexl 2005, 2006.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://boost.org/LICENSE_1_0.txt)

// Example use of the resource-constrained shortest paths algorithm.
#include <boost/config.hpp>

#ifdef BOOST_MSVC
#pragma warning(disable : 4267)
#endif

#include <boost/graph/adjacency_list.hpp>

#include <boost/graph/r_c_shortest_paths.hpp>
#include <iostream>

using namespace boost;

struct SPPRC_Example_Graph_Vert_Prop
{
  SPPRC_Example_Graph_Vert_Prop(int n = 0, int e = 0, int l = 0)
      : num(n), eat(e), lat(l) {}
  int num;
  // earliest arrival time
  int eat;
  // latest arrival time
  int lat;
};

struct SPPRC_Example_Graph_Arc_Prop
{
  SPPRC_Example_Graph_Arc_Prop(int n = 0, int c = 0, int t = 0)
      : num(n), cost(c), time(t) {}
  int num;
  // traversal cost
  int cost;
  // traversal time
  int time;
};

typedef adjacency_list<vecS,
                       vecS,
                       directedS,
                       SPPRC_Example_Graph_Vert_Prop,
                       SPPRC_Example_Graph_Arc_Prop>
    SPPRC_Example_Graph;

// data structures for spp without resource constraints:
// ResourceContainer model
struct spp_no_rc_res_cont
{
  spp_no_rc_res_cont(int c = 0) : cost(c){};
  spp_no_rc_res_cont &operator=(const spp_no_rc_res_cont &other)
  {
    if (this == &other)
      return *this;
    this->~spp_no_rc_res_cont();
    new (this) spp_no_rc_res_cont(other);
    return *this;
  }
  int cost;
};

bool operator==(const spp_no_rc_res_cont &res_cont_1,
                const spp_no_rc_res_cont &res_cont_2)
{
  return (res_cont_1.cost == res_cont_2.cost);
}

bool operator<(const spp_no_rc_res_cont &res_cont_1,
               const spp_no_rc_res_cont &res_cont_2)
{
  return (res_cont_1.cost < res_cont_2.cost);
}

// ResourceExtensionFunction model
class ref_no_res_cont
{
public:
  inline bool operator()(const SPPRC_Example_Graph &g,
                         spp_no_rc_res_cont &new_cont,
                         const spp_no_rc_res_cont &old_cont,
                         graph_traits<SPPRC_Example_Graph>::edge_descriptor ed) const
  {
    new_cont.cost = old_cont.cost + g[ed].cost;
    return true;
  }
};

// DominanceFunction model
class dominance_no_res_cont
{
public:
  inline bool operator()(const spp_no_rc_res_cont &res_cont_1,
                         const spp_no_rc_res_cont &res_cont_2) const
  {
    // must be "<=" here!!!
    // must NOT be "<"!!!
    return res_cont_1.cost <= res_cont_2.cost;
    // this is not a contradiction to the documentation
    // the documentation says:
    // "A label $l_1$ dominates a label $l_2$ if and only if both are resident
    // at the same vertex, and if, for each resource, the resource consumption
    // of $l_1$ is less than or equal to the resource consumption of $l_2$,
    // and if there is at least one resource where $l_1$ has a lower resource
    // consumption than $l_2$."
    // one can think of a new label with a resource consumption equal to that
    // of an old label as being dominated by that old label, because the new
    // one will have a higher number and is created at a later point in time,
    // so one can implicitly use the number or the creation time as a resource
    // for tie-breaking
  }
};
// end data structures for spp without resource constraints:

// data structures for shortest path problem with time windows (spptw)
// ResourceContainer model
struct spp_spptw_res_cont
{
  spp_spptw_res_cont(int c = 0, int t = 0) : cost(c), time(t) {}
  spp_spptw_res_cont &operator=(const spp_spptw_res_cont &other)
  {
    if (this == &other)
      return *this;
    this->~spp_spptw_res_cont();
    new (this) spp_spptw_res_cont(other);
    return *this;
  }
  int cost;
  int time;
};

bool operator==(const spp_spptw_res_cont &res_cont_1,
                const spp_spptw_res_cont &res_cont_2)
{
  return (res_cont_1.cost == res_cont_2.cost && res_cont_1.time == res_cont_2.time);
}

bool operator<(const spp_spptw_res_cont &res_cont_1,
               const spp_spptw_res_cont &res_cont_2)
{
  if (res_cont_1.cost > res_cont_2.cost)
    return false;
  if (res_cont_1.cost == res_cont_2.cost)
    return res_cont_1.time < res_cont_2.time;
  return true;
}

// ResourceExtensionFunction model
class ref_spptw
{
public:
  inline bool operator()(const SPPRC_Example_Graph &g,
                         spp_spptw_res_cont &new_cont,
                         const spp_spptw_res_cont &old_cont,
                         graph_traits<SPPRC_Example_Graph>::edge_descriptor ed) const
  {
    const SPPRC_Example_Graph_Arc_Prop &arc_prop =
        get(edge_bundle, g)[ed];
    const SPPRC_Example_Graph_Vert_Prop &vert_prop =
        get(vertex_bundle, g)[target(ed, g)];
    new_cont.cost = old_cont.cost + arc_prop.cost;
    int &i_time = new_cont.time;
    i_time = old_cont.time + arc_prop.time;
    i_time < vert_prop.eat ? i_time = vert_prop.eat : 0;
    return i_time <= vert_prop.lat ? true : false;
  }
};

// DominanceFunction model
class dominance_spptw
{
public:
  inline bool operator()(const spp_spptw_res_cont &res_cont_1,
                         const spp_spptw_res_cont &res_cont_2) const
  {
    // must be "<=" here!!!
    // must NOT be "<"!!!
    return res_cont_1.cost <= res_cont_2.cost && res_cont_1.time <= res_cont_2.time;
    // this is not a contradiction to the documentation
    // the documentation says:
    // "A label $l_1$ dominates a label $l_2$ if and only if both are resident
    // at the same vertex, and if, for each resource, the resource consumption
    // of $l_1$ is less than or equal to the resource consumption of $l_2$,
    // and if there is at least one resource where $l_1$ has a lower resource
    // consumption than $l_2$."
    // one can think of a new label with a resource consumption equal to that
    // of an old label as being dominated by that old label, because the new
    // one will have a higher number and is created at a later point in time,
    // so one can implicitly use the number or the creation time as a resource
    // for tie-breaking
  }
};
// end data structures for shortest path problem with time windows (spptw)

// example graph structure and cost from
// http://www.boost.org/libs/graph/example/dijkstra-example.cpp
enum nodes
{
  A,
  B,
  C,
  D,
  E
};
char name[] = "ABCDE";

int main()
{
  SPPRC_Example_Graph g;

  add_vertex(SPPRC_Example_Graph_Vert_Prop(A, 0, 0), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(B, 5, 20), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(C, 6, 10), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(D, 3, 12), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(E, 0, 100), g);

  add_edge(A, C, SPPRC_Example_Graph_Arc_Prop(0, 1, 5), g);
  add_edge(B, B, SPPRC_Example_Graph_Arc_Prop(1, 2, 5), g);
  add_edge(B, D, SPPRC_Example_Graph_Arc_Prop(2, 1, 2), g);
  add_edge(B, E, SPPRC_Example_Graph_Arc_Prop(3, 2, 7), g);
  add_edge(C, B, SPPRC_Example_Graph_Arc_Prop(4, 7, 3), g);
  add_edge(C, D, SPPRC_Example_Graph_Arc_Prop(5, 3, 8), g);
  add_edge(D, E, SPPRC_Example_Graph_Arc_Prop(6, 1, 3), g);
  add_edge(E, A, SPPRC_Example_Graph_Arc_Prop(7, 1, 5), g);
  add_edge(E, B, SPPRC_Example_Graph_Arc_Prop(8, 1, 4), g);

  // the unique shortest path from A to E in the dijkstra-example.cpp is
  // A -> C -> D -> E
  // its length is 5
  // the following code also yields this result

  // with the above time windows, this path is infeasible
  // now, there are two shortest paths that are also feasible with respect to
  // the vertex time windows:
  // A -> C -> B -> D -> E and
  // A -> C -> B -> E
  // however, the latter has a longer total travel time and is therefore not
  // pareto-optimal, i.e., it is dominated by the former path
  // therefore, the code below returns only the former path

  // spp without resource constraints
  graph_traits<SPPRC_Example_Graph>::vertex_descriptor s = A;
  graph_traits<SPPRC_Example_Graph>::vertex_descriptor t = E;

  std::vector<std::vector<graph_traits<SPPRC_Example_Graph>::edge_descriptor>>
      opt_solutions;
  std::vector<spp_no_rc_res_cont> pareto_opt_rcs_no_rc;

  r_c_shortest_paths(g,
                     get(&SPPRC_Example_Graph_Vert_Prop::num, g),
                     get(&SPPRC_Example_Graph_Arc_Prop::num, g),
                     s,
                     t,
                     opt_solutions,
                     pareto_opt_rcs_no_rc,
                     spp_no_rc_res_cont(0),
                     ref_no_res_cont(),
                     dominance_no_res_cont(),
                     std::allocator<r_c_shortest_paths_label<SPPRC_Example_Graph, spp_no_rc_res_cont>>(),
                     default_r_c_shortest_paths_visitor());

  std::cout << "SPP without resource constraints:" << std::endl;
  std::cout << "Number of optimal solutions: ";
  std::cout << static_cast<int>(opt_solutions.size()) << std::endl;
  for (int i = 0; i < static_cast<int>(opt_solutions.size()); ++i)
  {
    std::cout << "The " << i << "th shortest path from A to E is: ";
    std::cout << std::endl;
    for (int j = static_cast<int>(opt_solutions[i].size()) - 1; j >= 0; --j)
      std::cout << name[source(opt_solutions[i][j], g)] << std::endl;
    std::cout << "E" << std::endl;
    std::cout << "Length: " << pareto_opt_rcs_no_rc[i].cost << std::endl;
  }
  std::cout << std::endl;

  // spptw
  std::vector<std::vector<graph_traits<SPPRC_Example_Graph>::edge_descriptor>>
      opt_solutions_spptw;
  std::vector<spp_spptw_res_cont> pareto_opt_rcs_spptw;

  r_c_shortest_paths(g,
                     get(&SPPRC_Example_Graph_Vert_Prop::num, g),
                     get(&SPPRC_Example_Graph_Arc_Prop::num, g),
                     s,
                     t,
                     opt_solutions_spptw,
                     pareto_opt_rcs_spptw,
                     spp_spptw_res_cont(0, 0),
                     ref_spptw(),
                     dominance_spptw(),
                     std::allocator<r_c_shortest_paths_label<SPPRC_Example_Graph, spp_spptw_res_cont>>(),
                     default_r_c_shortest_paths_visitor());

  std::cout << "SPP with time windows:" << std::endl;
  std::cout << "Number of optimal solutions: ";
  std::cout << static_cast<int>(opt_solutions.size()) << std::endl;
  for (int i = 0; i < static_cast<int>(opt_solutions.size()); ++i)
  {
    std::cout << "The " << i << "th shortest path from A to E is: ";
    std::cout << std::endl;
    for (int j = static_cast<int>(opt_solutions_spptw[i].size()) - 1;
         j >= 0;
         --j)
      std::cout << name[source(opt_solutions_spptw[i][j], g)] << std::endl;
    std::cout << "E" << std::endl;
    std::cout << "Length: " << pareto_opt_rcs_spptw[i].cost << std::endl;
    std::cout << "Time: " << pareto_opt_rcs_spptw[i].time << std::endl;
  }

  // utility function check_r_c_path example
  std::cout << std::endl;
  bool b_is_a_path_at_all = false;
  bool b_feasible = false;
  bool b_correctly_extended = false;
  spp_spptw_res_cont actual_final_resource_levels(0, 0);
  graph_traits<SPPRC_Example_Graph>::edge_descriptor ed_last_extended_arc;
  check_r_c_path(g,
                 opt_solutions_spptw[0],
                 spp_spptw_res_cont(0, 0),
                 true,
                 pareto_opt_rcs_spptw[0],
                 actual_final_resource_levels,
                 ref_spptw(),
                 b_is_a_path_at_all,
                 b_feasible,
                 b_correctly_extended,
                 ed_last_extended_arc);
  if (!b_is_a_path_at_all)
    std::cout << "Not a path." << std::endl;
  if (!b_feasible)
    std::cout << "Not a feasible path." << std::endl;
  if (!b_correctly_extended)
    std::cout << "Not correctly extended." << std::endl;
  if (b_is_a_path_at_all && b_feasible && b_correctly_extended)
  {
    std::cout << "Actual final resource levels:" << std::endl;
    std::cout << "Length: " << actual_final_resource_levels.cost << std::endl;
    std::cout << "Time: " << actual_final_resource_levels.time << std::endl;
    std::cout << "OK." << std::endl;
  }

  return 0;
}


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "lu_lagrange.h"

// Cholesky LLT decomposition for symmetric positive definite
//#include <Eigen/SparseExtra>
// Bug in unsupported/Eigen/SparseExtra needs iostream first
#include <iostream>
#include <unsupported/Eigen/SparseExtra>
#include <cassert>
#include <cstdio>
#include "find.h"
#include "sparse.h"

template <typename T>
IGL_INLINE bool igl::lu_lagrange(
  const Eigen::SparseMatrix<T> & ATA,
  const Eigen::SparseMatrix<T> & C,
  Eigen::SparseMatrix<T> & L,
  Eigen::SparseMatrix<T> & U)
{
#if EIGEN_VERSION_AT_LEAST(3,0,92)
#  warning lu_lagrange has not yet been implemented for your Eigen Version
  return false;
#else
  // number of unknowns
  int n = ATA.rows();
  // number of lagrange multipliers
  int m = C.cols();

  assert(ATA.cols() == n);
  if(m != 0)
  {
    assert(C.rows() == n);
    if(C.nonZeros() == 0)
    {
      // See note above about empty columns in C
      fprintf(stderr,"Error: lu_lagrange() C has columns but no entries\n");
      return false;
    }
  }

  // Check that each column of C has at least one entry
  std::vector<bool> has_entry; has_entry.resize(C.cols(),false);
  // Iterate over outside
  for(int k=0; k<C.outerSize(); ++k)
  {
    // Iterate over inside
    for(typename Eigen::SparseMatrix<T>::InnerIterator it (C,k); it; ++it)
    {
      has_entry[it.col()] = true;
    }
  }
  for(int i=0;i<(int)has_entry.size();i++)
  {
    if(!has_entry[i])
    {
      // See note above about empty columns in C
      fprintf(stderr,"Error: lu_lagrange() C(:,%d) has no entries\n",i);
      return false;
    }
  }

  // Cholesky factorization of ATA
  //// Eigen fails if you give a full view of the matrix like this:
  //Eigen::SparseLLT<SparseMatrix<T> > ATA_LLT(ATA);
  Eigen::SparseMatrix<T> ATA_LT = ATA.template triangularView<Eigen::Lower>();
  Eigen::SparseLLT<Eigen::SparseMatrix<T> > ATA_LLT(ATA_LT);

  Eigen::SparseMatrix<T> J = ATA_LLT.matrixL();

  //if(!ATA_LLT.succeeded())
  if(!((J*0).eval().nonZeros() == 0))
  {
    fprintf(stderr,"Error: lu_lagrange() failed to factor ATA\n");
    return false;
  }

  if(m == 0)
  {
    // If there are no constraints (C is empty) then LU decomposition is just L
    // and L' from cholesky decomposition
    L = J;
    U = J.transpose();
  }else
  {
    // Construct helper matrix M
    Eigen::SparseMatrix<T> M = C;
    J.template triangularView<Eigen::Lower>().solveInPlace(M);

    // Compute cholesky factorizaiton of M'*M
    Eigen::SparseMatrix<T> MTM = M.transpose() * M;

    Eigen::SparseLLT<Eigen::SparseMatrix<T> > MTM_LLT(MTM.template triangularView<Eigen::Lower>());

    Eigen::SparseMatrix<T> K = MTM_LLT.matrixL();

    //if(!MTM_LLT.succeeded())
    if(!((K*0).eval().nonZeros() == 0))
    {
      fprintf(stderr,"Error: lu_lagrange() failed to factor MTM\n");
      return false;
    }

    // assemble LU decomposition of Q
    Eigen::Matrix<int,Eigen::Dynamic,1> MI;
    Eigen::Matrix<int,Eigen::Dynamic,1> MJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> MV;
    igl::find(M,MI,MJ,MV);

    Eigen::Matrix<int,Eigen::Dynamic,1> KI;
    Eigen::Matrix<int,Eigen::Dynamic,1> KJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> KV;
    igl::find(K,KI,KJ,KV);

    Eigen::Matrix<int,Eigen::Dynamic,1> JI;
    Eigen::Matrix<int,Eigen::Dynamic,1> JJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> JV;
    igl::find(J,JI,JJ,JV);

    int nnz = JV.size()  + MV.size() + KV.size();

    Eigen::Matrix<int,Eigen::Dynamic,1> UI(nnz);
    Eigen::Matrix<int,Eigen::Dynamic,1> UJ(nnz);
    Eigen::Matrix<T,Eigen::Dynamic,1> UV(nnz);
    UI << JJ,                        MI, (KJ.array() + n).matrix();
    UJ << JI, (MJ.array() + n).matrix(), (KI.array() + n).matrix();
    UV << JV,                        MV,                     KV*-1;
    igl::sparse(UI,UJ,UV,U);

    Eigen::Matrix<int,Eigen::Dynamic,1> LI(nnz);
    Eigen::Matrix<int,Eigen::Dynamic,1> LJ(nnz);
    Eigen::Matrix<T,Eigen::Dynamic,1> LV(nnz);
    LI << JI, (MJ.array() + n).matrix(), (KI.array() + n).matrix();
    LJ << JJ,                        MI, (KJ.array() + n).matrix();
    LV << JV,                        MV,                        KV;
    igl::sparse(LI,LJ,LV,L);
  }

  return true;
  #endif
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
template bool igl::lu_lagrange<double>(Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int>&, Eigen::SparseMatrix<double, 0, int>&);
#endif


/******************************************************
    Author : shipeng_liu
    Email : 1196075299@qq.com
    Description: Adaptive dynamic programming
******************************************************/
#include <cmath>
#include<iostream>
#include <fstream>
#include <Eigen/Dense>
#include <iomanip>

using namespace std;

/******************************************************************
    Critic Network
    Input: S, diff_s, steer_cmd
    number of hidden_layer: 1
    number of active_function: hidden_number
    output: J_cost
    parameter_layer1: parameters from input to hidden_layer
    parameter_layer2: parameters from hidden_layer to output
******************************************************************/

class critic_network {

  private:

    double learning_rate_basic = 0.1 ; //learning rate
    double reward_count = 0.95; //the reward count
    Eigen::MatrixXd parameter_layer1;
    Eigen::MatrixXd parameter_layer2;
    Eigen::MatrixXd input_variable;
    Eigen::MatrixXd hidden_layer;
    Eigen::MatrixXd p_hidden_layer;
    double output;
    double reference_signal;
    double last_reference_signal;
    int hidden_number;
    static double active_function(double input) {
        // 1 - exp( - qi(t) ) / 1 + exp( - qi(t) ) )
        double ret = std::exp(- input);
        double tmp = (2)/(1 + ret) - 1;
        return tmp;
    }

  public:
    static critic_network& Get_critic_network()
    {
        static critic_network singleton;
        return singleton;
    }

    critic_network()
    {
        hidden_number = 4;
        init();
    }
    double return_J_cost()
    {
        return output;
    }
    double return_reference()
    {
        return reference_signal;
    }
    Eigen::MatrixXd return_parameter_layer1()
    {
        return parameter_layer1;
    }
    Eigen::MatrixXd return_parameter_layer2()
    {
        return parameter_layer2;
    }
    Eigen::MatrixXd return_p_hidden_layer()
    {
        return p_hidden_layer;
    }
    void init()
    {
        parameter_layer1 = Eigen::MatrixXd::Random(3, hidden_number);
        parameter_layer2 = Eigen::MatrixXd::Random(hidden_number, 1);

        hidden_layer = Eigen::MatrixXd::Random(1, hidden_number);
        p_hidden_layer = Eigen::MatrixXd::Random(1, hidden_number);
        input_variable = Eigen::MatrixXd::Random(1,3);
        output = 0;

    }

    double output_J_cost(double s, double diff_s,double steer_cmd)
    {
        input_variable(0,0) = s;
        input_variable(0,1) = diff_s;
        input_variable(0,2) = steer_cmd;
        hidden_layer = input_variable * parameter_layer1;
        // limit hidden layer

        for (int i = 0; i < hidden_number; i++)
        {
            p_hidden_layer(0,i) = active_function(hidden_layer(0,i));
            // limit the p_Hidden_layer
            if (p_hidden_layer(0,i) < 0.000001 && p_hidden_layer(0,i) > 0 )
                p_hidden_layer(0,i) = 0.000001;
            else if (p_hidden_layer(0,i) > -0.000001 && p_hidden_layer(0,i) < 0)
                p_hidden_layer(0,i) = - 0.000001;
        }

        Eigen::MatrixXd test = p_hidden_layer * parameter_layer2;
        output = test(0,0);
        // limit the J_cost
        if (output < 0)
        {
            output = 0;
        }

        return output;
    }

    void update_weight(double J_cost, double Last_J_cost, double refer_s)
    {
        // update learning rate:
        last_reference_signal = reference_signal;
        reference_signal =  refer_s;
        //double learning_rate = learning_rate_basic * (1+2/(1+fabs(reference_signal - last_reference_signal)) );
        double learning_rate = learning_rate_basic * 4;
        double ect = reward_count*J_cost -  reference_signal;
        //double reference_signal = 0.5;
        cout << "\ncritic_network_information:\n";
        cout << left << setw(20) << "[ refer: " << right
             << setw(20) << reference_signal << "]" << endl;
        cout << left << setw(20) << "[ J_cost: " << right
             << setw(20) << J_cost << "]" << endl;
        cout << left << setw(20) << "[ ect: " << right
             << setw(20) << ect << "]" << endl;
        cout << "[ p_hidden_layer: " << endl;
        cout << p_hidden_layer << endl;
        // cout <<  "[ parameter_layer1: " << endl;
        // cout << parameter_layer1 << endl;

        // cout << "[ parameter_layer2: " << endl;
        // cout << parameter_layer2 << endl;

        for (int j = 0; j < hidden_number; j++)
        {

            for (int k = 0; k < 3; k++)
            {

                double derivative_1 = reward_count * ect * parameter_layer2(j,0) * 0.5 * (1 - pow(p_hidden_layer(0,j),2)) * input_variable(0,k);

                double delta_weight1 = learning_rate * (- derivative_1);

                parameter_layer1(k,j) += delta_weight1;
            }
            double derivative =  reward_count * ect * (p_hidden_layer(0,j));
            double delta_weight = learning_rate * (- derivative);
            parameter_layer2(j,0) += delta_weight;

        }
    }
};


// Copyright 2019 Xanadu Quantum Technologies Inc.

// 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.
/**
 * @file
 * Contains functions for calculating the multidimensional
 * Hermite polynomials, used for computation of batched hafnians.
 */

#pragma once
#include <stdafx.h>

#include <Eigen/Eigenvalues>
#include <Eigen/LU>

typedef unsigned long long int ullint;

/**
 * Returns the index of the one dimensional flattened vector corresponding to the multidimensional tensor
 *
 * @param pos
 * @param resolution
 *
 * @return index on flattened vector
 */
ullint vec2index(std::vector<int> &pos, int resolution) {
    int dim = pos.size();
    ullint nextCoordinate = 0;

    nextCoordinate = pos[0]-1;
    for(int ii = 0; ii < dim-1; ii++) {
        nextCoordinate = nextCoordinate*resolution + (pos[ii+1]-1);
    }

    return nextCoordinate;

}

/**
 * Returns the indices of the tensor corresponding to a given element
 *
 * @param val
 * @param base
 * @param n
 *
 * @return tensor index
 */
std::vector<int> find_rep(int val, int base, int n) {
    std::vector<int> x(n, 0);
    int local_val = val;

    x[0] = 1;

    for (int i = 1; i < n; i++)
        x[i] = x[i-1]*base;

    std::vector<int> digits(n, 0);

    for (int i = 0; i < n; i++) {
        digits[i] = local_val/x[n-i-1];
        local_val = local_val - digits[i] * x[n-i-1];
    }

    return digits;
}

/**
 * Returns the sqrt of the factorial of an integer.
 *
 * @param nn input integer
 *
 * @return Square root of the factorial of \f$n\f$.
 */
long double sqrtfactorial(int nn)
{
    long double n = static_cast<long double>(nn);

    if(n > 1)
        return std::sqrt(n) * sqrtfactorial(n - 1);
    else
        return 1;
}

/**
 * Renormalizes an unnormalized photon number statistics of a Gaussian state.
 * Based on the MATLAB code available at: https://github.com/clementsw/gaussian-optics
 *
 * @param tn unnormalized flattened vector of size \f$res**nmodes$ representing unnormalized photon number statistics
 *       \f$2n\times 2n\f$ row-ordered symmetric matrix.
 * @param nmodes number of modes
 * @param res highest number of photons to be resolved.
 *
 * @return Renormalized photon number statistics
 */
template <typename T>
inline std::vector<T> renormalization(std::vector<T> tn, int nmodes, int res) {
    std::vector<long double> invsqfacts(res, 0);
    std::vector<int> digits(nmodes, 0);

    ullint Hdim = pow(res, nmodes);

    for (int i = 0; i < res; i++)
        invsqfacts[i] = sqrtfactorial(i);

    for (ullint i = 0; i < Hdim; i++) {
        digits = find_rep(i, res, nmodes);
        long double pref = 1;
        for (int j = 0; j < nmodes; j++)
            pref *= 1.0L/invsqfacts[digits[j]];
        tn[i] = tn[i]*static_cast<double>(pref);
    }

    return tn;

}

namespace libwalrus {

/**
 * Returns photon number statistics of a Gaussian state for a given covariance matrix `mat`.
 * as described in *Multidimensional Hermite polynomials and photon distribution for polymode mixed light*
 * [arxiv:9308033](https://arxiv.org/abs/hep-th/9308033).
 *
 * This implementation is based on the MATLAB code available at
 * https://github.com/clementsw/gaussian-optics
 *
 * @param mat a flattened vector of size \f$2n^2\f$, representing an
 *       \f$2n\times 2n\f$ row-ordered symmetric matrix.
 * @param d a flattened vector of size \f$2n\f$, representing the first order moments.
 * @param resolution highest number of photons to be resolved.
 *
 */
template <typename T>
inline std::vector<T> hermite_multidimensional_cpp(std::vector<T> &R_mat, std::vector<T> &y_mat, int &resolution, int &renorm) {
    int dim = std::sqrt(static_cast<double>(R_mat.size()));

    namespace eg = Eigen;

    eg::Matrix<T, eg::Dynamic, eg::Dynamic> R = eg::Map<eg::Matrix<T, eg::Dynamic, eg::Dynamic>, eg::Unaligned>(R_mat.data(), dim, dim);
    eg::Matrix<T, eg::Dynamic, eg::Dynamic> y = eg::Map<eg::Matrix<T, eg::Dynamic, eg::Dynamic>, eg::Unaligned>(y_mat.data(), dim, dim);

    ullint Hdim = pow(resolution, dim);
    std::vector<T> H(Hdim, 0);
    std::vector<double> ren_factor(Hdim, 0);

    H[0] = 1;
    ren_factor[0] = 1;

    std::vector<int> nextPos(dim, 1);
    std::vector<int> jumpFrom(dim, 1);
    std::vector<int> ek(dim, 0);
    std::vector<double> factors(resolution+1, 0);
    int jump = 0;

    for (ullint jj = 0; jj < Hdim-1; jj++) {

        if (jump > 0) {
            jumpFrom[jump] += 1;
            jump = 0;
        }

        for (int ii = 0; ii < dim; ii++) {
            std::vector<int> forwardStep(dim, 0);
            forwardStep[ii] = 1;

            if ( forwardStep[ii] + nextPos[ii] > resolution) {
                nextPos[ii] = 1;
                jumpFrom[ii] = 1;
                jump = ii+1;
            }
            else {
                jumpFrom[ii] = nextPos[ii];
                nextPos[ii] = nextPos[ii] + 1;
                break;
            }
        }

        for (int ii = 0; ii < dim; ii++)
            ek[ii] = nextPos[ii] - jumpFrom[ii];

        int k = 0;
        for(; k < static_cast<int>(ek.size()); k++) {
            if(ek[k]) break;
        }

        ullint nextCoordinate = vec2index(nextPos, resolution);
        ullint fromCoordinate = vec2index(jumpFrom, resolution);

        for (int ii = 0; ii < dim; ii++) {
            H[nextCoordinate] = H[nextCoordinate] + R(k, ii) * y(ii, 0);
        }
        H[nextCoordinate] = H[nextCoordinate] * H[fromCoordinate];

        std::vector<int> tmpjump(dim, 0);

        for (int ii = 0; ii < dim; ii++) {
            if (jumpFrom[ii] > 1) {
                std::vector<int> prevJump(dim, 0);
                prevJump[ii] = 1;
                std::transform(jumpFrom.begin(), jumpFrom.end(), prevJump.begin(), tmpjump.begin(), std::minus<int>());
                ullint prevCoordinate = vec2index(tmpjump, resolution);
                H[nextCoordinate] = H[nextCoordinate] - (static_cast<T>(jumpFrom[ii]-1))*static_cast<T>(R(k,ii))*H[prevCoordinate];

            }
        }

    }

    if (renorm) {
        H = renormalization(H, dim, resolution);
    }

    return H;

}

}


//=======================================================================
// Copyright 2013 Maciej Piechotka
// Authors: Maciej Piechotka
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef BOOST_GRAPH_EDGE_COLORING_HPP
#define BOOST_GRAPH_EDGE_COLORING_HPP

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/properties.hpp>
#include <algorithm>
#include <limits>
#include <vector>

/* This algorithm is to find coloring of an edges

   Reference:

   Misra, J., & Gries, D. (1992). A constructive proof of Vizing's
   theorem. In Information Processing Letters.
*/

namespace boost {
  namespace detail {
    template<typename Graph, typename ColorMap>
    bool
    is_free(const Graph &g,
            ColorMap color,
            typename boost::graph_traits<Graph>::vertex_descriptor u,
            typename boost::property_traits<ColorMap>::value_type free_color)
    {
      typedef typename boost::property_traits<ColorMap>::value_type color_t;
      if (free_color == (std::numeric_limits<color_t>::max)())
        return false;
      BGL_FORALL_OUTEDGES_T(u, e, g, Graph) {
        if (get(color, e) == free_color) {
          return false;
        }
      }
      return true;
    }

    template<typename Graph, typename ColorMap>
    std::vector<typename boost::graph_traits<Graph>::vertex_descriptor>
    maximal_fan(const Graph &g,
                ColorMap color,
                typename boost::graph_traits<Graph>::vertex_descriptor x,
                typename boost::graph_traits<Graph>::vertex_descriptor y)
    {
      typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
      std::vector<vertex_t> fan;
      fan.push_back(y);
      bool extended;
      do {
        extended = false;
        BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
          vertex_t v = target(e, g);
          if (is_free(g, color, fan.back(), get(color, e)) &&
              std::find(fan.begin(), fan.end(), v) == fan.end()) {
            fan.push_back(v);
            extended = true;
          }
        }
      } while(extended);
      return fan;
    }
    template<typename Graph, typename ColorMap>
    typename boost::property_traits<ColorMap>::value_type
    find_free_color(const Graph &g,
                    ColorMap color,
                    typename boost::graph_traits<Graph>::vertex_descriptor u)
    {
      typename boost::property_traits<ColorMap>::value_type c = 0;
      while (!is_free(g, color, u, c)) c++;
      return c;
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::graph_traits<Graph>::edge_descriptor eold,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      put(color, eold, d);
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d && e != eold) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }

    template<typename Graph, typename ColorMap, typename ForwardIterator>
    void
    rotate_fan(const Graph &g,
               ColorMap color,
               typename boost::graph_traits<Graph>::vertex_descriptor x,
               ForwardIterator begin,
               ForwardIterator end)
    {
      typedef typename boost::graph_traits<Graph>::edge_descriptor edge_t;
      if (begin == end) {
        return;
      }
      edge_t previous = edge(x, *begin, g).first;
      for (begin++; begin != end; begin++) {
        edge_t current = edge(x, *begin, g).first;
        put(color, previous, get(color, current));
        previous = current;
      }
    }

    template<typename Graph, typename ColorMap>
    class find_free_in_fan
    {
    public:
      find_free_in_fan(const Graph &graph,
                       const ColorMap color,
                       typename boost::property_traits<ColorMap>::value_type d)
        : graph(graph),
          color(color),
          d(d) {}
      bool operator()(const typename boost::graph_traits<Graph>::vertex_descriptor u) const {
        return is_free(graph, color, u, d);
      }
    private:
      const Graph &graph;
      const ColorMap color;
      const typename boost::property_traits<ColorMap>::value_type d;
    };
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  color_edge(const Graph &g,
             ColorMap color,
             typename boost::graph_traits<Graph>::edge_descriptor e)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    typedef typename std::vector<vertex_t>::iterator fan_iterator;
    using namespace detail;
    vertex_t x = source(e, g), y = target(e, g);
    std::vector<vertex_t> fan = maximal_fan(g, color, x, y);
    color_t c = find_free_color(g, color, x);
    color_t d = find_free_color(g, color, fan.back());
    invert_cd_path(g, color, x, c, d);
    fan_iterator w = std::find_if(fan.begin(),
                                  fan.end(),
                                  find_free_in_fan<Graph, ColorMap>(g, color, d));
    rotate_fan(g, color, x, fan.begin(), w + 1);
    put(color, edge(x, *w, g).first, d);
    return (std::max)(c, d);
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  edge_coloring(const Graph &g,
                ColorMap color)
  {
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      put(color, e, (std::numeric_limits<color_t>::max)());
    }
    color_t colors = 0;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      colors = (std::max)(colors, color_edge(g, color, e) + 1);
    }
    return colors;
  }
}

#endif