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

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

 * See LICENSE for the license information

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

/**
 * @file DecisionTreeFactor.cpp
 * @brief discrete factor
 * @date Feb 14, 2011
 * @author Duy-Nguyen Ta
 * @author Frank Dellaert
 */

#include <gtsam/discrete/DecisionTreeFactor.h>
#include <gtsam/discrete/DiscreteConditional.h>
#include <gtsam/base/FastSet.h>

#include <boost/make_shared.hpp>

using namespace std;

namespace gtsam {

  /* ******************************************************************************** */
  DecisionTreeFactor::DecisionTreeFactor() {
  }

  /* ******************************************************************************** */
  DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys& keys,
      const ADT& potentials) :
      DiscreteFactor(keys.indices()), Potentials(keys, potentials) {
  }

  /* *************************************************************************/
  DecisionTreeFactor::DecisionTreeFactor(const DiscreteConditional& c) :
      DiscreteFactor(c.keys()), Potentials(c) {
  }

  /* ************************************************************************* */
  bool DecisionTreeFactor::equals(const DiscreteFactor& other, double tol) const {
    if(!dynamic_cast<const DecisionTreeFactor*>(&other)) {
      return false;
    }
    else {
      const DecisionTreeFactor& f(static_cast<const DecisionTreeFactor&>(other));
      return Potentials::equals(f, tol);
    }
  }

  /* ************************************************************************* */
  void DecisionTreeFactor::print(const string& s,
      const KeyFormatter& formatter) const {
    cout << s;
    Potentials::print("Potentials:",formatter);
  }

  /* ************************************************************************* */
  DecisionTreeFactor DecisionTreeFactor::apply(const DecisionTreeFactor& f,
    ADT::Binary op) const {
    map<Key,size_t> cs; // new cardinalities
    // make unique key-cardinality map
    for(Key j: keys()) cs[j] = cardinality(j);
    for(Key j: f.keys()) cs[j] = f.cardinality(j);
    // Convert map into keys
    DiscreteKeys keys;
    for(const std::pair<const Key,size_t>& key: cs)
      keys.push_back(key);
    // apply operand
    ADT result = ADT::apply(f, op);
    // Make a new factor
    return DecisionTreeFactor(keys, result);
  }

  /* ************************************************************************* */
  DecisionTreeFactor::shared_ptr DecisionTreeFactor::combine(size_t nrFrontals,
    ADT::Binary op) const {

    if (nrFrontals > size()) throw invalid_argument(
        (boost::format(
            "DecisionTreeFactor::combine: invalid number of frontal keys %d, nr.keys=%d")
            % nrFrontals % size()).str());

    // sum over nrFrontals keys
    size_t i;
    ADT result(*this);
    for (i = 0; i < nrFrontals; i++) {
      Key j = keys()[i];
      result = result.combine(j, cardinality(j), op);
    }

    // create new factor, note we start keys after nrFrontals
    DiscreteKeys dkeys;
    for (; i < keys().size(); i++) {
      Key j = keys()[i];
      dkeys.push_back(DiscreteKey(j,cardinality(j)));
    }
    return boost::make_shared<DecisionTreeFactor>(dkeys, result);
  }

  /* ************************************************************************* */
  DecisionTreeFactor::shared_ptr DecisionTreeFactor::combine(const Ordering& frontalKeys,
    ADT::Binary op) const {

    if (frontalKeys.size() > size()) throw invalid_argument(
        (boost::format(
            "DecisionTreeFactor::combine: invalid number of frontal keys %d, nr.keys=%d")
            % frontalKeys.size() % size()).str());

    // sum over nrFrontals keys
    size_t i;
    ADT result(*this);
    for (i = 0; i < frontalKeys.size(); i++) {
      Key j = frontalKeys[i];
      result = result.combine(j, cardinality(j), op);
    }

    // create new factor, note we collect keys that are not in frontalKeys
    // TODO: why do we need this??? result should contain correct keys!!!
    DiscreteKeys dkeys;
    for (i = 0; i < keys().size(); i++) {
      Key j = keys()[i];
      // TODO: inefficient!
      if (std::find(frontalKeys.begin(), frontalKeys.end(), j) != frontalKeys.end())
        continue;
      dkeys.push_back(DiscreteKey(j,cardinality(j)));
    }
    return boost::make_shared<DecisionTreeFactor>(dkeys, result);
  }

  /* ************************************************************************* */
  std::vector<std::pair<DiscreteValues, double>> DecisionTreeFactor::enumerate() const {
    // Get all possible assignments
    std::vector<std::pair<Key, size_t>> pairs;
    for (auto& key : keys()) {
      pairs.emplace_back(key, cardinalities_.at(key));
    }
    // Reverse to make cartesianProduct output a more natural ordering.
    std::vector<std::pair<Key, size_t>> rpairs(pairs.rbegin(), pairs.rend());
    const auto assignments = cartesianProduct(rpairs);

    // Construct unordered_map with values
    std::vector<std::pair<DiscreteValues, double>> result;
    for (const auto& assignment : assignments) {
      result.emplace_back(assignment, operator()(assignment));
    }
    return result;
  }

  /* ************************************************************************* */
  std::string DecisionTreeFactor::markdown(
      const KeyFormatter& keyFormatter) const {
    std::stringstream ss;

    // Print out header and construct argument for `cartesianProduct`.
    ss << "|";
    for (auto& key : keys()) {
      ss << keyFormatter(key) << "|";
    }
    ss << "value|\n";

    // Print out separator with alignment hints.
    ss << "|";
    for (size_t j = 0; j < size(); j++) ss << ":-:|";
    ss << ":-:|\n";

    // Print out all rows.
    auto rows = enumerate();
    for (const auto& kv : rows) {
      ss << "|";
      auto assignment = kv.first;
      for (auto& key : keys()) ss << assignment.at(key) << "|";
      ss << kv.second << "|\n";
    }
    return ss.str();
  }

  /* ************************************************************************* */
} // namespace gtsam


//---------------------------------------------------------------------------//
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

#include <boost/compute/system.hpp>
#include <boost/compute/algorithm/fill.hpp>
#include <boost/compute/algorithm/reduce_by_key.hpp>
#include <boost/compute/container/vector.hpp>

#include "perf.hpp"

int rand_int()
{
    return static_cast<int>((rand() / double(RAND_MAX)) * 25.0);
}

struct unique_key {
  int current;
  int avgValuesNoPerKey;

  unique_key()
  {
      current = 0;
      avgValuesNoPerKey = 512;
  }

  int operator()()
  {
      double p = double(1.0) / static_cast<double>(avgValuesNoPerKey);
      if((rand() / double(RAND_MAX)) <= p)
          return ++current;
      return current;
  }
} UniqueKey;

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

    std::cout << "size: " << PERF_N << std::endl;

    // setup context and queue for the default device
    boost::compute::device device = boost::compute::system::default_device();
    boost::compute::context context(device);
    boost::compute::command_queue queue(context, device);
    std::cout << "device: " << device.name() << std::endl;

    // create vector of keys and random values
    std::vector<int> host_keys(PERF_N);
    std::vector<int> host_values(PERF_N);
    std::generate(host_keys.begin(), host_keys.end(), UniqueKey);
    std::generate(host_values.begin(), host_values.end(), rand_int);

    // create vectors for keys and values on the device and copy the data
    boost::compute::vector<int> device_keys(PERF_N, context);
    boost::compute::vector<int> device_values(PERF_N,context);
    boost::compute::copy(
        host_keys.begin(),
        host_keys.end(),
        device_keys.begin(),
        queue
    );
    boost::compute::copy(
        host_values.begin(),
        host_values.end(),
        device_values.begin(),
        queue
    );

    // vectors for the results
    boost::compute::vector<int> device_keys_results(PERF_N, context);
    boost::compute::vector<int> device_values_results(PERF_N,context);

    typedef boost::compute::vector<int>::iterator iterType;
    std::pair<iterType, iterType> result(
        device_keys_results.begin(),
        device_values_results.begin()
    );

    // reduce by key
    perf_timer t;
    for(size_t trial = 0; trial < PERF_TRIALS; trial++){
        t.start();
        result = boost::compute::reduce_by_key(device_keys.begin(),
                                               device_keys.end(),
                                               device_values.begin(),
                                               device_keys_results.begin(),
                                               device_values_results.begin(),
                                               queue);
        t.stop();
    }
    std::cout << "time: " << t.min_time() / 1e6 << " ms" << std::endl;

    size_t result_size = std::distance(device_keys_results.begin(), result.first);
    if(result_size != static_cast<size_t>(host_keys[PERF_N-1] + 1)){
        std::cout << "ERROR: "
                  << "wrong number of keys" << result_size << "\n" << (host_keys[PERF_N-1] + 1)
                  << std::endl;
        return -1;
    }

    return 0;
}


#ifndef STAN_MATH_PRIM_PROB_RAYLEIGH_RNG_HPP
#define STAN_MATH_PRIM_PROB_RAYLEIGH_RNG_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/size.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <cmath>

namespace stan {
namespace math {

/** \ingroup prob_dists
 * Return a Rayleigh random variate with scale parameter sigma
 * using the specified random number generator.
 *
 * sigma can be a scalar or a one-dimensional container.
 *
 * @tparam T_scale Type of scale parameter
 * @tparam RNG class of random number generator
 * @param sigma (Sequence of) positive scale parameter(s)
 * @param rng random number generator
 * @return (Sequence of) Rayleigh random variate(s)
 * @throw std::domain_error if sigma is nonpositive
 */
template <typename T_scale, class RNG>
inline typename VectorBuilder<true, double, T_scale>::type rayleigh_rng(
    const T_scale& sigma, RNG& rng) {
  using boost::variate_generator;
  using boost::random::uniform_real_distribution;
  static const char* function = "rayleigh_rng";
  check_positive_finite(function, "Scale parameter", sigma);

  scalar_seq_view<T_scale> sigma_vec(sigma);
  size_t N = stan::math::size(sigma);
  VectorBuilder<true, double, T_scale> output(N);

  variate_generator<RNG&, uniform_real_distribution<> > uniform_rng(
      rng, uniform_real_distribution<>(0.0, 1.0));
  for (size_t n = 0; n < N; ++n) {
    output[n] = sigma_vec[n] * std::sqrt(-2.0 * std::log(uniform_rng()));
  }

  return output.data();
}

}  // namespace math
}  // namespace stan
#endif


/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2019, University of Stuttgart
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the University of Stuttgart nor the names
 *     of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written
 *     permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

/* Author: Andreas Orthey */

const double edgeWidth = 0.1;
const double runtime_limit = 10;
const double memory_limit = 4096 * 4096;
const int run_count = 10;
unsigned int curDim = 8;
int numberPlanners = 0;

#include "QuotientSpacePlanningCommon.h"
#include <ompl/base/spaces/RealVectorStateSpace.h>

#include <ompl/geometric/planners/informedtrees/BITstar.h>
#include <ompl/geometric/planners/est/BiEST.h>
#include <ompl/geometric/planners/est/EST.h>
#include <ompl/geometric/planners/est/ProjEST.h>
#include <ompl/geometric/planners/fmt/BFMT.h>
#include <ompl/geometric/planners/fmt/FMT.h>
#include <ompl/geometric/planners/kpiece/BKPIECE1.h>
#include <ompl/geometric/planners/kpiece/KPIECE1.h>
#include <ompl/geometric/planners/kpiece/LBKPIECE1.h>
#include <ompl/geometric/planners/pdst/PDST.h>
// #include <ompl/geometric/planners/prm/LazyPRM.h> //TODO: segfault?
#include <ompl/geometric/planners/prm/LazyPRMstar.h>
#include <ompl/geometric/planners/prm/PRM.h>
#include <ompl/geometric/planners/prm/PRMstar.h>
#include <ompl/geometric/planners/prm/SPARS.h>
#include <ompl/geometric/planners/prm/SPARStwo.h>
#include <ompl/geometric/planners/quotientspace/QRRT.h>
#include <ompl/geometric/planners/rrt/BiTRRT.h>
#include <ompl/geometric/planners/rrt/InformedRRTstar.h>
#include <ompl/geometric/planners/rrt/LazyRRT.h>
#include <ompl/geometric/planners/rrt/LBTRRT.h>
#include <ompl/geometric/planners/rrt/RRTConnect.h>
#include <ompl/geometric/planners/rrt/RRT.h>
#include <ompl/geometric/planners/rrt/RRTsharp.h>
#include <ompl/geometric/planners/rrt/RRTstar.h>
#include <ompl/geometric/planners/rrt/RRTXstatic.h>
#include <ompl/geometric/planners/rrt/SORRTstar.h>
#include <ompl/geometric/planners/rrt/TRRT.h>
#include <ompl/geometric/planners/sbl/pSBL.h>
#include <ompl/geometric/planners/sbl/SBL.h>
#include <ompl/geometric/planners/sst/SST.h>
#include <ompl/geometric/planners/stride/STRIDE.h>

#include <ompl/tools/benchmark/Benchmark.h>
#include <ompl/util/String.h>

#include <boost/math/constants/constants.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
#include <boost/format.hpp>
#include <fstream>

// Only states near some edges of a hypercube are valid. The valid edges form a
// narrow passage from (0,...,0) to (1,...,1). A state s is valid if there exists
// a k s.t. (a) 0<=s[k]<=1, (b) for all i<k s[i]<=edgeWidth, and (c) for all i>k
// s[i]>=1-edgewidth.
class HyperCubeValidityChecker : public ob::StateValidityChecker
{
public:
    HyperCubeValidityChecker(const ob::SpaceInformationPtr &si, int dimension)
      : ob::StateValidityChecker(si), dimension_(dimension)
    {
        si->setStateValidityCheckingResolution(0.001);
    }

    bool isValid(const ob::State *state) const override
    {
        const auto *s = static_cast<const ob::RealVectorStateSpace::StateType *>(state);
        bool foundMaxDim = false;

        for (int i = dimension_ - 1; i >= 0; i--)
            if (!foundMaxDim)
            {
                if ((*s)[i] > edgeWidth)
                    foundMaxDim = true;
            }
            else if ((*s)[i] < (1. - edgeWidth))
                return false;
        return true;
    }

protected:
    int dimension_;
};

static unsigned int numberRuns{0};

void PostRunEvent(const ob::PlannerPtr &planner, ot::Benchmark::RunProperties &run)
{
    static unsigned int pid = 0;

    ob::SpaceInformationPtr si = planner->getSpaceInformation();
    ob::ProblemDefinitionPtr pdef = planner->getProblemDefinition();

    unsigned int states = boost::lexical_cast<int>(run["graph states INTEGER"]);
    double time = boost::lexical_cast<double>(run["time REAL"]);
    double memory = boost::lexical_cast<double>(run["memory REAL"]);

    bool solved = (time < runtime_limit);

    std::cout << "Run " << pid << "/" << numberRuns << " [" << planner->getName() << "] "
              << (solved ? "solved" : "FAILED") << "(time: " << time << ", states: " << states << ", memory: " << memory
              << ")" << std::endl;
    std::cout << std::string(80, '-') << std::endl;
    pid++;
}

// Note: Number of all simplifications is
// unsigned int numberSimplifications = std::pow(2, curDim - 1);
// But here we will only create three simplifications, the trivial one, the
// discrete one and a two-step simplifications, which we found worked well in
// this experiment. You can experiment with finding better simplifications.
// std::cout << "dimension: " << curDim << " simplifications:" << numberSimplifications << std::endl;

std::vector<std::vector<int>> getHypercubeAdmissibleProjections(int dim)
{
    std::vector<std::vector<int>> projections;

    // trivial: just configuration space
    // discrete: use all admissible projections
    std::vector<int> trivial{dim};
    std::vector<int> discrete;
    boost::push_back(discrete, boost::irange(2, dim + 1));

    std::vector<int> twoStep;
    boost::push_back(twoStep, boost::irange(2, dim + 1, 2));
    if (twoStep.back() != dim)
        twoStep.push_back(dim);

    projections.push_back(trivial);
    projections.push_back(discrete);
    projections.push_back(twoStep);
    auto last = std::unique(projections.begin(), projections.end());
    projections.erase(last, projections.end());

    // std::cout << "Projections for dim " << dim << std::endl;
    // for(unsigned int k = 0; k < projections.size(); k++){
    //     std::vector<int> pk = projections.at(k);
    //     std::cout << k << ": ";
    //     for(unsigned int j = 0; j < pk.size(); j++){
    //       std::cout << pk.at(j) << (j<pk.size()-1?",":"");
    //     }
    //     std::cout << std::endl;
    // }

    return projections;
}

void addPlanner(ompl::tools::Benchmark &benchmark, const ompl::base::PlannerPtr &planner, double range)
{
    ompl::base::ParamSet &params = planner->params();
    if (params.hasParam(std::string("range")))
        params.setParam(std::string("range"), ompl::toString(range));
    benchmark.addPlanner(planner);
    numberPlanners++;
}

ob::PlannerPtr GetQRRT(std::vector<int> sequenceLinks, ob::SpaceInformationPtr si)
{
    // ompl::msg::setLogLevel(ompl::msg::LOG_DEV2);
    std::vector<ob::SpaceInformationPtr> si_vec;

    for (unsigned int k = 0; k < sequenceLinks.size() - 1; k++)
    {
        int links = sequenceLinks.at(k);

        auto spaceK(std::make_shared<ompl::base::RealVectorStateSpace>(links));
        ompl::base::RealVectorBounds bounds(links);
        bounds.setLow(0.);
        bounds.setHigh(1.);
        spaceK->setBounds(bounds);

        auto siK = std::make_shared<ob::SpaceInformation>(spaceK);
        siK->setStateValidityChecker(std::make_shared<HyperCubeValidityChecker>(siK, links));

        spaceK->setup();
        si_vec.push_back(siK);
    }
    si_vec.push_back(si);

    auto planner = std::make_shared<og::QRRT>(si_vec);
    std::string qName = "QuotientSpaceRRT[";
    for (unsigned int k = 0; k < sequenceLinks.size() - 1; k++)
    {
        int links = sequenceLinks.at(k);
        qName += std::to_string(links) + ",";
    }
    qName += std::to_string(si->getStateDimension());
    qName += "]";
    std::cout << qName << std::endl;
    planner->setName(qName);
    return planner;
}

int main(int argc, char **argv)
{
    if (argc > 1)
    {
        curDim = std::atoi(argv[1]);
    }

    numberPlanners = 0;
    double range = edgeWidth * 0.5;
    auto space(std::make_shared<ompl::base::RealVectorStateSpace>(curDim));
    ompl::base::RealVectorBounds bounds(curDim);
    ompl::geometric::SimpleSetup ss(space);
    ob::SpaceInformationPtr si = ss.getSpaceInformation();
    ob::ProblemDefinitionPtr pdef = ss.getProblemDefinition();
    ompl::base::ScopedState<> start(space), goal(space);

    bounds.setLow(0.);
    bounds.setHigh(1.);
    space->setBounds(bounds);
    ss.setStateValidityChecker(std::make_shared<HyperCubeValidityChecker>(si, curDim));
    for (unsigned int i = 0; i < curDim; ++i)
    {
        start[i] = 0.;
        goal[i] = 1.;
    }
    ss.setStartAndGoalStates(start, goal);

    ot::Benchmark benchmark(ss, "HyperCube");
    benchmark.addExperimentParameter("num_dims", "INTEGER", std::to_string(curDim));

    //############################################################################
    // Load All Planner
    //############################################################################
    std::vector<std::vector<int>> admissibleProjections = getHypercubeAdmissibleProjections(curDim);
    for (unsigned int k = 0; k < admissibleProjections.size(); k++)
    {
        std::vector<int> proj = admissibleProjections.at(k);
        ob::PlannerPtr quotientSpacePlannerK = GetQRRT(proj, si);
        addPlanner(benchmark, quotientSpacePlannerK, range);
    }
    addPlanner(benchmark, std::make_shared<og::BITstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::EST>(si), range);
    addPlanner(benchmark, std::make_shared<og::BiEST>(si), range);
    addPlanner(benchmark, std::make_shared<og::ProjEST>(si), range);
    addPlanner(benchmark, std::make_shared<og::FMT>(si), range);
    addPlanner(benchmark, std::make_shared<og::BFMT>(si), range);
    addPlanner(benchmark, std::make_shared<og::KPIECE1>(si), range);
    addPlanner(benchmark, std::make_shared<og::BKPIECE1>(si), range);
    addPlanner(benchmark, std::make_shared<og::LBKPIECE1>(si), range);
    addPlanner(benchmark, std::make_shared<og::PDST>(si), range);
    addPlanner(benchmark, std::make_shared<og::PRM>(si), range);
    addPlanner(benchmark, std::make_shared<og::PRMstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::LazyPRMstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::SPARS>(si), range);
    addPlanner(benchmark, std::make_shared<og::SPARStwo>(si), range);
    addPlanner(benchmark, std::make_shared<og::RRT>(si), range);
    addPlanner(benchmark, std::make_shared<og::RRTConnect>(si), range);
    addPlanner(benchmark, std::make_shared<og::RRTsharp>(si), range);
    addPlanner(benchmark, std::make_shared<og::RRTstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::RRTXstatic>(si), range);
    addPlanner(benchmark, std::make_shared<og::LazyRRT>(si), range);
    addPlanner(benchmark, std::make_shared<og::InformedRRTstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::TRRT>(si), range);
    addPlanner(benchmark, std::make_shared<og::BiTRRT>(si), range);
    addPlanner(benchmark, std::make_shared<og::LBTRRT>(si), range);
    addPlanner(benchmark, std::make_shared<og::SORRTstar>(si), range);
    addPlanner(benchmark, std::make_shared<og::SBL>(si), range);
    addPlanner(benchmark, std::make_shared<og::SST>(si), range);
    addPlanner(benchmark, std::make_shared<og::STRIDE>(si), range);
    //############################################################################

    printEstimatedTimeToCompletion(numberPlanners, run_count, runtime_limit);

    ot::Benchmark::Request request;
    request.maxTime = runtime_limit;
    request.maxMem = memory_limit;
    request.runCount = run_count;
    request.simplify = false;
    request.displayProgress = false;
    numberRuns = numberPlanners * run_count;

    benchmark.setPostRunEvent(std::bind(&PostRunEvent, std::placeholders::_1, std::placeholders::_2));
    benchmark.benchmark(request);
    benchmark.saveResultsToFile(boost::str(boost::format("hypercube_%i.log") % curDim).c_str());

    printBenchmarkResults(benchmark);

    return 0;
}


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

  Defines the as_floating meta-function

  @copyright 2016 NumScale SAS

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

**/
//==================================================================================================
#ifndef BOOST_SIMD_DETAIL_DISPATCH_META_AS_FLOATING_HPP_INCLUDED
#define BOOST_SIMD_DETAIL_DISPATCH_META_AS_FLOATING_HPP_INCLUDED

#include <boost/simd/detail/dispatch/meta/factory_of.hpp>
#include <boost/simd/detail/dispatch/meta/primitive_of.hpp>
#include <boost/simd/detail/dispatch/meta/make_floating.hpp>
#include <type_traits>

namespace boost { namespace dispatch
{
  namespace detail
  {
    template<typename T> struct as_floating
    {
      using type  = boost::dispatch::make_floating_t< sizeof(boost::dispatch::primitive_of_t<T>)
                                                    , boost::dispatch::factory_of<T>
                                                    >;
    };
  }

  /*!
    @ingroup group-generation
    @brief Turn a type into an equivalent floating point type

    Converts any type which Primitive is Fundamental into a floating point type with a compatible
    size .

    @tparam T     Type to convert.
  **/
  template<typename T>
  struct as_floating : detail::as_floating<typename std::decay<T>::type>
  {};

  template<typename T>
  using as_floating_t = typename as_floating<T>::type;
} }

#endif


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2014 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland.

// This file was modified by Oracle on 2014.
// Modifications copyright (c) 2014, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP

#include <iterator>

#include <boost/core/ignore_unused.hpp>
#include <boost/range.hpp>
#include <boost/type_traits/is_same.hpp>

#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/exterior_ring.hpp>
#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>

#include <boost/geometry/util/math.hpp>

#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/tags.hpp>

#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/covered_by.hpp>
#include <boost/geometry/algorithms/within.hpp>

#include <boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp>
#include <boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp>
#include <boost/geometry/algorithms/detail/distance/is_comparable.hpp>
#include <boost/geometry/algorithms/detail/distance/iterator_selector.hpp>

#include <boost/geometry/algorithms/dispatch/distance.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace distance
{

template <typename P1, typename P2, typename Strategy>
struct point_to_point
{
    static inline
    typename strategy::distance::services::return_type<Strategy, P1, P2>::type
    apply(P1 const& p1, P2 const& p2, Strategy const& strategy)
    {
        boost::ignore_unused(strategy);
        return strategy.apply(p1, p2);
    }
};

template
<
    typename Point,
    typename Range,
    closure_selector Closure,
    typename Strategy
>
class point_to_range
{
private:
    typedef typename strategy::distance::services::comparable_type
        <
            Strategy
        >::type comparable_strategy;

    typedef detail::closest_feature::point_to_point_range
        <
            Point, Range, Closure, comparable_strategy
        > point_to_point_range;

public:
    typedef typename strategy::distance::services::return_type
        <
            Strategy,
            Point,
            typename boost::range_value<Range>::type
        >::type return_type;

    static inline return_type apply(Point const& point, Range const& range,
                                    Strategy const& strategy)
    {
        return_type const zero = return_type(0);

        if (boost::size(range) == 0)
        {
            return zero;
        }

        namespace sds = strategy::distance::services;

        typename sds::return_type
            <
                comparable_strategy,
                Point,
                typename point_type<Range>::type
            >::type cd_min;

        std::pair
            <
                typename boost::range_iterator<Range const>::type,
                typename boost::range_iterator<Range const>::type
            > it_pair
            = point_to_point_range::apply(point,
                                          boost::begin(range),
                                          boost::end(range),
                                          sds::get_comparable
                                              <
                                                  Strategy
                                              >::apply(strategy),
                                          cd_min);

        return
            is_comparable<Strategy>::value
            ?
            cd_min
            :
            strategy.apply(point, *it_pair.first, *it_pair.second);
    }
};

template
<
    typename Point,
    typename Ring,
    closure_selector Closure,
    typename Strategy
>
struct point_to_ring
{
    typedef typename strategy::distance::services::return_type
        <
            Strategy, Point, typename point_type<Ring>::type
        >::type return_type;

    static inline return_type apply(Point const& point,
                                    Ring const& ring,
                                    Strategy const& strategy)
    {
        if (geometry::within(point, ring))
        {
            return return_type(0);
        }

        return point_to_range
            <
                Point, Ring, closure<Ring>::value, Strategy
            >::apply(point, ring, strategy);
    }
};

template
<
    typename Point,
    typename Polygon,
    closure_selector Closure,
    typename Strategy
>
class point_to_polygon
{
public:
    typedef typename strategy::distance::services::return_type
        <
            Strategy, Point, typename point_type<Polygon>::type
        >::type return_type;

private:
    typedef point_to_range
        <
            Point, typename ring_type<Polygon>::type, Closure, Strategy
        > per_ring;

    struct distance_to_interior_rings
    {
        template <typename InteriorRingIterator>
        static inline return_type apply(Point const& point,
                                        InteriorRingIterator first,
                                        InteriorRingIterator last,
                                        Strategy const& strategy)
        {
            for (InteriorRingIterator it = first; it != last; ++it)
            {
                if (geometry::within(point, *it))
                {
                    // the point is inside a polygon hole, so its distance
                    // to the polygon its distance to the polygon's
                    // hole boundary
                    return per_ring::apply(point, *it, strategy);
                }
            }
            return 0;
        }

        template <typename InteriorRings>
        static inline return_type apply(Point const& point,
                                        InteriorRings const& interior_rings,
                                        Strategy const& strategy)
        {
            return apply(point,
                         boost::begin(interior_rings),
                         boost::end(interior_rings),
                         strategy);
        }
    };

public:
    static inline return_type apply(Point const& point,
                                    Polygon const& polygon,
                                    Strategy const& strategy)
    {
        if (!geometry::covered_by(point, exterior_ring(polygon)))
        {
            // the point is outside the exterior ring, so its distance
            // to the polygon is its distance to the polygon's exterior ring
            return per_ring::apply(point, exterior_ring(polygon), strategy);
        }

        // Check interior rings
        return distance_to_interior_rings::apply(point,
                                                 interior_rings(polygon),
                                                 strategy);
    }
};

template
<
    typename Point,
    typename MultiGeometry,
    typename Strategy,
    bool CheckCoveredBy = boost::is_same
        <
            typename tag<MultiGeometry>::type, multi_polygon_tag
        >::value
>
class point_to_multigeometry
{
private:
    typedef detail::closest_feature::geometry_to_range geometry_to_range;

public:
    typedef typename strategy::distance::services::return_type
        <
            Strategy,
            Point,
            typename point_type<MultiGeometry>::type
        >::type return_type;

    static inline return_type apply(Point const& point,
                                    MultiGeometry const& multigeometry,
                                    Strategy const& strategy)
    {
        typedef iterator_selector<MultiGeometry const> selector_type;

        namespace sds = strategy::distance::services;

        typename sds::return_type
            <
                typename sds::comparable_type<Strategy>::type,
                Point,
                typename point_type<MultiGeometry>::type
            >::type cd;

        typename selector_type::iterator_type it_min
            = geometry_to_range::apply(point,
                                       selector_type::begin(multigeometry),
                                       selector_type::end(multigeometry),
                                       sds::get_comparable
                                           <
                                               Strategy
                                           >::apply(strategy),
                                       cd);

        return
            is_comparable<Strategy>::value
            ?
            cd
            :
            dispatch::distance
                <
                    Point,
                    typename std::iterator_traits
                        <
                            typename selector_type::iterator_type
                        >::value_type,
                    Strategy
                >::apply(point, *it_min, strategy);
    }
};

// this is called only for multipolygons, hence the change in the
// template parameter name MultiGeometry to MultiPolygon
template <typename Point, typename MultiPolygon, typename Strategy>
struct point_to_multigeometry<Point, MultiPolygon, Strategy, true>
{
    typedef typename strategy::distance::services::return_type
        <
            Strategy,
            Point,
            typename point_type<MultiPolygon>::type
        >::type return_type;

    static inline return_type apply(Point const& point,
                                    MultiPolygon const& multipolygon,
                                    Strategy const& strategy)
    {
        if (geometry::covered_by(point, multipolygon))
        {
            return 0;
        }

        return point_to_multigeometry
            <
                Point, MultiPolygon, Strategy, false
            >::apply(point, multipolygon, strategy);
    }
};

}} // namespace detail::distance
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

// Point-point
template <typename P1, typename P2, typename Strategy>
struct distance
    <
        P1, P2, Strategy, point_tag, point_tag,
        strategy_tag_distance_point_point, false
    > : detail::distance::point_to_point<P1, P2, Strategy>
{};

// Point-line version 2, where point-segment strategy is specified
template <typename Point, typename Linestring, typename Strategy>
struct distance
    <
        Point, Linestring, Strategy, point_tag, linestring_tag,
        strategy_tag_distance_point_segment, false
    > : detail::distance::point_to_range<Point, Linestring, closed, Strategy>
{};

// Point-ring , where point-segment strategy is specified
template <typename Point, typename Ring, typename Strategy>
struct distance
    <
        Point, Ring, Strategy, point_tag, ring_tag,
        strategy_tag_distance_point_segment, false
    > : detail::distance::point_to_ring
        <
            Point, Ring, closure<Ring>::value, Strategy
        >
{};

// Point-polygon , where point-segment strategy is specified
template <typename Point, typename Polygon, typename Strategy>
struct distance
    <
        Point, Polygon, Strategy, point_tag, polygon_tag,
        strategy_tag_distance_point_segment, false
    > : detail::distance::point_to_polygon
        <
            Point, Polygon, closure<Polygon>::value, Strategy
        >
{};

// Point-segment version 2, with point-segment strategy
template <typename Point, typename Segment, typename Strategy>
struct distance
    <
        Point, Segment, Strategy, point_tag, segment_tag,
        strategy_tag_distance_point_segment, false
    >
{
    static inline typename strategy::distance::services::return_type
        <
            Strategy, Point, typename point_type<Segment>::type
        >::type apply(Point const& point,
                      Segment const& segment,
                      Strategy const& strategy)
    {
        typename point_type<Segment>::type p[2];
        geometry::detail::assign_point_from_index<0>(segment, p[0]);
        geometry::detail::assign_point_from_index<1>(segment, p[1]);

        boost::ignore_unused(strategy);
        return strategy.apply(point, p[0], p[1]);
    }
};

template <typename Point, typename Box, typename Strategy>
struct distance
    <
         Point, Box, Strategy, point_tag, box_tag,
         strategy_tag_distance_point_box, false
    >
{
    static inline typename strategy::distance::services::return_type
        <
            Strategy, Point, typename point_type<Box>::type
        >::type
    apply(Point const& point, Box const& box, Strategy const& strategy)
    {
        boost::ignore_unused(strategy);
        return strategy.apply(point, box);
    }
};

template<typename Point, typename MultiPoint, typename Strategy>
struct distance
    <
        Point, MultiPoint, Strategy, point_tag, multi_point_tag,
        strategy_tag_distance_point_point, false
    > : detail::distance::point_to_multigeometry
        <
            Point, MultiPoint, Strategy
        >
{};

template<typename Point, typename MultiLinestring, typename Strategy>
struct distance
    <
        Point, MultiLinestring, Strategy, point_tag, multi_linestring_tag,
        strategy_tag_distance_point_segment, false
    > : detail::distance::point_to_multigeometry
        <
            Point, MultiLinestring, Strategy
        >
{};

template<typename Point, typename MultiPolygon, typename Strategy>
struct distance
    <
        Point, MultiPolygon, Strategy, point_tag, multi_polygon_tag,
        strategy_tag_distance_point_segment, false
    > : detail::distance::point_to_multigeometry
        <
            Point, MultiPolygon, Strategy
        >
{};

template <typename Point, typename Linear, typename Strategy>
struct distance
    <
         Point, Linear, Strategy, point_tag, linear_tag,
         strategy_tag_distance_point_segment, false
    > : distance
        <
            Point, Linear, Strategy,
            point_tag, typename tag<Linear>::type,
            strategy_tag_distance_point_segment, false
        >
{};

template <typename Point, typename Areal, typename Strategy>
struct distance
    <
         Point, Areal, Strategy, point_tag, areal_tag,
         strategy_tag_distance_point_segment, false
    > : distance
        <
            Point, Areal, Strategy,
            point_tag, typename tag<Areal>::type,
            strategy_tag_distance_point_segment, false
        >
{};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP


#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_SET_SIZE 40

#include <rapidxml_ns/rapidxml_ns.hpp>
#include <svgpp/policy/xml/rapidxml_ns.hpp>
#include <svgpp/document_traversal.hpp>
#include <boost/mpl/set.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

namespace mpl = boost::mpl;

class Filter;
class feComponentTransfer;

class ElementWithRegion
{
public:
  void set(svgpp::tag::attribute::x, double val)  { x_ = val; }
  void set(svgpp::tag::attribute::y, double val)  { y_ = val; }
  void set(svgpp::tag::attribute::width, double val) { width_ = val; }
  void set(svgpp::tag::attribute::height, double val) { height_ = val; }

protected:
  double x_, y_, width_, height_;
};

enum FilterInput { fiSourceGraphic, fiSourceAlpha, fiBackgroundImage,
  fiBackgroundAlpha, fiFillPaint, fiStrokePaint, fiReference };

typedef mpl::map<
  mpl::pair< svgpp::tag::value::SourceGraphic,    mpl::integral_c<FilterInput, fiSourceGraphic> >,
  mpl::pair< svgpp::tag::value::SourceAlpha,      mpl::integral_c<FilterInput, fiSourceAlpha> >,
  mpl::pair< svgpp::tag::value::BackgroundImage,  mpl::integral_c<FilterInput, fiBackgroundImage> >,
  mpl::pair< svgpp::tag::value::BackgroundAlpha,  mpl::integral_c<FilterInput, fiBackgroundAlpha> >,
  mpl::pair< svgpp::tag::value::FillPaint,        mpl::integral_c<FilterInput, fiFillPaint> >,
  mpl::pair< svgpp::tag::value::StrokePaint,      mpl::integral_c<FilterInput, fiStrokePaint> >
> filter_input_to_enum;

template<class InAttributeTag>
class ElementWithIn
{
public:
  template<class Input>
  void set(InAttributeTag, Input)
  {
    filterInput_ = mpl::at<filter_input_to_enum, Input>::type::value;
  }

  void set(InAttributeTag, boost::iterator_range<const char *> const & reference)
  {
    filterInput_ = fiReference;
    reference_.assign(boost::begin(reference), boost::end(reference));
  }

protected:
  FilterInput filterInput_;
  std::string reference_;
};

class FilterElement: public ElementWithRegion
{
public:
  using ElementWithRegion::set;

  template<class Range>
  void set(svgpp::tag::attribute::result, Range const & r)
  {
    result_.assign(boost::begin(r), boost::end(r));
  }

protected:
  std::string result_;
};

class feBlend:
  public FilterElement,
  public ElementWithIn<svgpp::tag::attribute::in>,
  public ElementWithIn<svgpp::tag::attribute::in2>
{
  enum BlendMode { mNormal, mMultiply, mScreen, mDarken, mLighten};

  typedef mpl::map<
    mpl::pair< svgpp::tag::value::normal,   mpl::integral_c<BlendMode, mNormal> >,
    mpl::pair< svgpp::tag::value::multiply, mpl::integral_c<BlendMode, mMultiply> >,
    mpl::pair< svgpp::tag::value::screen,   mpl::integral_c<BlendMode, mScreen> >,
    mpl::pair< svgpp::tag::value::darken,   mpl::integral_c<BlendMode, mDarken> >,
    mpl::pair< svgpp::tag::value::lighten,  mpl::integral_c<BlendMode, mLighten> >
  > mode_to_enum;

public:
  feBlend(Filter & parent)
    : parent_(parent)
  {
  }

  void on_exit_element();

  using FilterElement::set;
  using ElementWithIn<svgpp::tag::attribute::in>::set;
  using ElementWithIn<svgpp::tag::attribute::in2>::set;

  template<class Mode>
  void set(svgpp::tag::attribute::mode, Mode)
  {
    mode_ = mpl::at<mode_to_enum, Mode>::type::value;
  }

private:
  Filter & parent_;
  BlendMode mode_;
};

struct feFuncData
{
  double slope_, intercept_, amplitude_, exponent_, offset_;
  std::vector<double> tableValues_;
};

class feFunc
{
public:
  feFunc(feComponentTransfer & parent)
    : parent_(parent)
  {}

  template<class Type>
  void set(svgpp::tag::attribute::type, Type)
  {
  }

  template<class Range>
  void set(svgpp::tag::attribute::tableValues, Range const & range)
  {
    data_.tableValues_.assign(boost::begin(range), boost::end(range));
  }

  void set(svgpp::tag::attribute::slope, double value) { data_.slope_ = value; }
  void set(svgpp::tag::attribute::intercept, double value) { data_.intercept_ = value; }
  void set(svgpp::tag::attribute::amplitude, double value) { data_.amplitude_ = value; }
  void set(svgpp::tag::attribute::exponent, double value) { data_.exponent_ = value; }
  void set(svgpp::tag::attribute::offset, double value) { data_.offset_ = value; }

protected:
  feComponentTransfer & parent_;
  feFuncData data_;
};

enum ARGBComponent { argbA, argbR, argbG, argbB };

class feComponentTransfer:
  public FilterElement,
  public ElementWithIn<svgpp::tag::attribute::in>
{
public:
  feComponentTransfer(Filter & parent)
    : parent_(parent)
  {
  }

  void on_exit_element() const {}

  void setComponentFunction(ARGBComponent component, feFuncData const & func)
  {
    func_[component] = func;
  }

  using FilterElement::set;
  using ElementWithIn<svgpp::tag::attribute::in>::set;

private:
  Filter & parent_;
  feFuncData func_[4];
};

template<ARGBComponent ComponentArg>
class feFuncX: public feFunc
{
public:
  static const ARGBComponent Component = ComponentArg;

  feFuncX(feComponentTransfer & parent)
    : feFunc(parent)
  {}

  void on_exit_element()
  {
    parent_.setComponentFunction(Component, data_);
  }
};

class Filter: public ElementWithRegion
{
public:
  Filter()
  {}

  void on_exit_element()
  {
  }

  using ElementWithRegion::set;

  template<class StringRange>
  void set(svgpp::tag::attribute::id, StringRange const & str)
  {
    id_.assign(boost::begin(str), boost::end(str));
  }

  void set(svgpp::tag::attribute::filterUnits, svgpp::tag::value::userSpaceOnUse)
  {
    filterUnitsUseObjectBoundingBox_ = false;
  }

  void set(svgpp::tag::attribute::filterUnits, svgpp::tag::value::objectBoundingBox)
  {
    filterUnitsUseObjectBoundingBox_ = true;
  }

private:
  std::string id_;
  bool filterUnitsUseObjectBoundingBox_;
};

void feBlend::on_exit_element()
{
}

class Canvas
{
public:
  void on_enter_element(svgpp::tag::element::svg const &) {}
  void on_exit_element() {}

  boost::shared_ptr<Filter> get_child_context(svgpp::tag::element::filter const &)
  {
    return boost::make_shared<Filter>();
  }
};

struct ContextFactories
{
  template<class ParentContext, class ElementTag>
  struct apply
  {
    typedef svgpp::factory::context::same<ParentContext, ElementTag> type;
  };
};

template<class ParentContext>
struct ContextFactories::apply<ParentContext, svgpp::tag::element::filter>
{
  typedef svgpp::factory::context::get_ptr_from_parent<svgpp::tag::element::filter, boost::shared_ptr<Filter> > type;
};

template<class ParentContext>
struct ContextFactories::apply<ParentContext, svgpp::tag::element::feBlend>
{
  typedef svgpp::factory::context::on_stack<feBlend> type;
};

template<class ParentContext>
struct ContextFactories::apply<ParentContext, svgpp::tag::element::feComponentTransfer>
{
  typedef svgpp::factory::context::on_stack<feComponentTransfer> type;
};

template<>
struct ContextFactories::apply<feComponentTransfer, svgpp::tag::element::feFuncA>
{
  typedef svgpp::factory::context::on_stack<feFuncX<argbA> > type;
};

template<>
struct ContextFactories::apply<feComponentTransfer, svgpp::tag::element::feFuncR>
{
  typedef svgpp::factory::context::on_stack<feFuncX<argbR> > type;
};

template<>
struct ContextFactories::apply<feComponentTransfer, svgpp::tag::element::feFuncG>
{
  typedef svgpp::factory::context::on_stack<feFuncX<argbG> > type;
};

template<>
struct ContextFactories::apply<feComponentTransfer, svgpp::tag::element::feFuncB>
{
  typedef svgpp::factory::context::on_stack<feFuncX<argbB> > type;
};

int main()
{
  char text[] = "<svg/>";
  rapidxml_ns::xml_document<> doc;    // character type defaults to char
  doc.parse<0>(text);
  if (rapidxml_ns::xml_node<> * svg_element = doc.first_node("svg"))
  {
    Canvas canvas;
    svgpp::document_traversal<
      svgpp::context_factories<ContextFactories>,
      svgpp::processed_elements<
        boost::mpl::set<
          svgpp::tag::element::svg,
          svgpp::tag::element::filter,
          svgpp::tag::element::feMergeNode,
          svgpp::tag::element::feDistantLight,
          svgpp::tag::element::fePointLight,
          svgpp::tag::element::feSpotLight,
          svgpp::tag::element::feComponentTransfer,
          svgpp::tag::element::feFuncA,
          svgpp::tag::element::feFuncB,
          svgpp::tag::element::feFuncG,
          svgpp::tag::element::feFuncR
        >::type
      >,
      svgpp::processed_attributes<
        boost::mpl::set<
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::id>,
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::x>,
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::y>,
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::width>,
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::height>,
          boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::filterUnits>,
          //boost::mpl::pair<svgpp::tag::element::filter, svgpp::tag::attribute::primitiveUnits>,

          svgpp::tag::attribute::result,
          svgpp::tag::attribute::in,
          svgpp::tag::attribute::in2,
          boost::mpl::pair<svgpp::tag::element::feBlend, svgpp::tag::attribute::x>,
          boost::mpl::pair<svgpp::tag::element::feBlend, svgpp::tag::attribute::y>,
          boost::mpl::pair<svgpp::tag::element::feBlend, svgpp::tag::attribute::width>,
          boost::mpl::pair<svgpp::tag::element::feBlend, svgpp::tag::attribute::height>,
          boost::mpl::pair<svgpp::tag::element::feBlend, svgpp::tag::attribute::mode>,
          boost::mpl::pair<svgpp::tag::element::feColorMatrix, svgpp::tag::attribute::type>,
          boost::mpl::pair<svgpp::tag::element::feColorMatrix, svgpp::tag::attribute::values>,

          // transfer function element attributes
          boost::mpl::pair<svgpp::tag::element::feFuncA, svgpp::tag::attribute::type>,
          boost::mpl::pair<svgpp::tag::element::feFuncR, svgpp::tag::attribute::type>,
          boost::mpl::pair<svgpp::tag::element::feFuncG, svgpp::tag::attribute::type>,
          boost::mpl::pair<svgpp::tag::element::feFuncB, svgpp::tag::attribute::type>,
          svgpp::tag::attribute::tableValues,
          svgpp::tag::attribute::slope,
          svgpp::tag::attribute::intercept,
          svgpp::tag::attribute::amplitude,
          svgpp::tag::attribute::exponent,
          svgpp::tag::attribute::offset
        >::type
      >
    >::load_document(svg_element, canvas);
  }
  return 0;
}