text:
#include <gtest/gtest.h>
#include <string>
#include <stdexcept>
#include <Eigen/Dense>
#include <cmath>

// testing following API
#include "estimation/UnscentedKalmanFilter.h"
#include "probability/sampling.h"

using namespace std;
using namespace estimation;
using namespace probability;

namespace UnscentedKalmanFilterTest
{
  void f(VectorXd& x, const VectorXd& u)
  {
    x[0] = x[0] + u[0];
  }
  void h(VectorXd& z, const VectorXd& x)
  {
    z = x;
  }

  void f2(VectorXd& x, const VectorXd& u)
  {
    VectorXd x_apriori(x.size());

    x_apriori[0] = x[0] + 0.1*x[1];
    x_apriori[1] = x[1];

    x = x_apriori;
  }
  void h2(VectorXd& z, const VectorXd& x)
  {
    z[0] = x[1];
  }

  //
  // Simple nonlinear system: a robot drives with constant velocity
  // and constant heading. Distance made per cycle and heading are
  // measured directly. The measurements are generated with the
  // sampling functions available.
  //
  void fn(VectorXd& x, const VectorXd& u)
  {
    VectorXd x_apriori(x.size());

    x_apriori[0] = x[0] + x[1]*cos(x[2]);	// x
    x_apriori[1] = x[1];			// ds
    x_apriori[2] = x[2];			// th

    x = x_apriori;
  }

  void hn(VectorXd& z, const VectorXd& x)
  {
    z[0] = x[1];	// ds
    z[1] = x[2];	// th
  }

  // -----------------------------------------
  // tests
  // -----------------------------------------
  TEST(UnscentedKalmanFilterTest, initialization)
  {
    UnscentedKalmanFilter ukf;

    // check setting required params
    EXPECT_THROW(ukf.validate(), IEstimator::estimator_error);	// "STM missing"
    ukf.setStateTransitionModel(f);
    EXPECT_THROW(ukf.validate(), IEstimator::estimator_error);	// "OM missing"
    ukf.setObservationModel(h);
    EXPECT_THROW(ukf.validate(), IEstimator::estimator_error);	// "PNC missing"
    MatrixXd Q(1,1); Q << 0.1;
    ukf.setProcessNoiseCovariance(Q);
    EXPECT_THROW(ukf.validate(), IEstimator::estimator_error);	// "MNC missing"
    MatrixXd R(1,1); R << 10;
    ukf.setMeasurementNoiseCovariance(R);
    EXPECT_NO_THROW(ukf.validate());			// all required params given

    // check setting optional params
    // optional params doesn't set new sizes
    MatrixXd P0_f(2,2); P0_f << 1, 0, 0, 1;
    ukf.setInitialErrorCovariance(P0_f);
    EXPECT_NO_THROW(ukf.validate());		// validate resets P0 to zero-matrix of correct size
    EXPECT_EQ(ukf.getState().size(), 1);	// correct size = 1 (see Q)

    VectorXd x_f(2); x_f << 0,1;
    ukf.setInitialState(x_f);
    EXPECT_NO_THROW(ukf.validate());		// validate resets x0 to zero-vector of correct size
    EXPECT_NE(ukf.getState().size(), 2);	// correct size = 1 (see Q)

    // required param Q sets new size of x and out
    MatrixXd Q2(2,2); Q2 << 0.1, 0, 0, 0.1;
    ukf.setProcessNoiseCovariance(Q2);
    EXPECT_NO_THROW(ukf.validate());
    EXPECT_EQ(ukf.getState().size(), 2);
    EXPECT_EQ(ukf.getLastEstimate().size(), 2);

    // check initialization of output
    Output out = ukf.getLastEstimate();
    OutputValue defaultOutVal;
    EXPECT_GT(out.size(), 0);
    EXPECT_DOUBLE_EQ(out.getValue(), defaultOutVal.getValue());

    // check setting the control input
    InputValue ctrl(0);
    Input in_ctrl(ctrl);
    EXPECT_THROW(ukf.setControlInput(in_ctrl), length_error);
    ukf.setControlInputSize(1);
    EXPECT_NO_THROW(ukf.setControlInput(in_ctrl));
  }

/*
  TEST(UnscentedKalmanFilterTest, validation)
  {
    // TODO more validation if not changed...
  }

  TEST(UnscentedKalmanFilterTest, validationEffect)
  {
    // TODO more validation if not changed...
  }
*/

  TEST(UnscentedKalmanFilterTest, functionality)
  {
    UnscentedKalmanFilter ukf;

    ukf.setStateTransitionModel(f);
    ukf.setObservationModel(h);
    MatrixXd Q(1,1); Q << 0.1;
    ukf.setProcessNoiseCovariance(Q);
    MatrixXd R(1,1); R << 10;
    ukf.setMeasurementNoiseCovariance(R);

    ukf.validate();

    // check if the calculation of an estimate is correct
    InputValue measurement(1);
    Input in(measurement);
    Output out;

    // u contained in a state transition formula, so an error will be
    // thrown when u is not initialized with the correct size
    //out = ekf.estimate(in);		// fail assertion produced by Eigen (out-of-range error)
    ukf.setControlInputSize(1);
    ukf.validate();

    // setting a control input = 0 does not change the result, it can
    // be calculated as if there is no control input
    InputValue ctrl(0);
    Input in_ctrl(ctrl);
    ukf.setControlInput(in_ctrl);	// (no validation needed, cannot be validated)

    EXPECT_NO_THROW(out = ukf.estimate(in));

    EXPECT_EQ(out.size(), 1);
    EXPECT_NEAR(out[0].getValue(), 0.0099, 0.0001);
    EXPECT_NEAR(out[0].getVariance(), 0.0990, 0.0001);

    // missing measurement
    InputValue missingValue;
    Input inMissing(missingValue);
    EXPECT_NO_THROW(out = ukf.estimate(inMissing));

    EXPECT_NEAR(out[0].getValue(), 0.0099, 0.0001);
  }

  TEST(UnscentedKalmanFilterTest, functionalityNonlinear)
  {
    UnscentedKalmanFilter ukf;
    const double heading = 80*M_PI/180, distance = 1;

    ukf.setStateTransitionModel(fn);
    ukf.setObservationModel(hn);
    MatrixXd Q(3,3);
    Q << 0, 0, 0,
      0, 0.01, 0,
      0, 0, 0.01;
    ukf.setProcessNoiseCovariance(Q);
    ukf.setInitialErrorCovariance(Q);
    MatrixXd R(2,2);
    R << 0.05, 0,
      0, 0.03;
    ukf.setMeasurementNoiseCovariance(R);
    VectorXd x0(3);
    x0 << 0, distance, heading;
    ukf.setInitialState(x0);
    ukf.validate();

    // estimate
    VectorXd z(2);
    VectorXd zMean(2); zMean << distance, heading;
    MatrixXd zCov(2,2); zCov << 0.05, 0, 0, 0.03;

    InputValue ds(distance);
    InputValue th(heading);
    Input in;
    in.add(ds);
    in.add(th);
    Output out;

    // estimate with exact values
    out = ukf.estimate(in);
    EXPECT_NEAR(out[0].getValue(), distance*cos(heading), 0.1);
    EXPECT_NEAR(out[1].getValue(), distance, 0.05);
    EXPECT_NEAR(out[2].getValue(), heading, 0.03);

    // estimate with sample measurements
    ukf.setInitialState(x0);
    ukf.validate();

    for (int i = 0; i < 20; i++)
    {
      // sample measurements
      z = sampleNormalDistribution(zMean, zCov);
      in[0].setValue(z[0]);
      in[1].setValue(z[1]);

      out = ukf.estimate(in);
    }

    EXPECT_NEAR(out[0].getValue(), 20*(distance*cos(heading)), 1);
    EXPECT_NEAR(out[1].getValue(), distance, 0.5);
    EXPECT_NEAR(out[2].getValue(), heading, 0.3);
  }
}

max_stars_repo_name: tuw-cpsg/sf-pkg

max_issues_repo_name: ros-agriculture/sf-pkg

max_forks_repo_name: tuw-cpsg/sf-pkg


text:
// Fits servo model using least squares
// Author: Max Schwarz <max.schwarz@uni-bonn.de>

#include "commandfitter.h"
#include "trajectory.h"
#include <servomodel/servocommandgenerator.h>
#include "valuelogger.h"

#include <float.h>

#include <Eigen/SVD>

using namespace Eigen;

CommandFitter::CommandFitter(ServoCommandGenerator* gen)
 : m_gen(gen)
 , m_min(DBL_MIN)
 , m_max(DBL_MAX)
{
}

CommandFitter::~CommandFitter()
{
}

void CommandFitter::setCommandLimits(double min, double max)
{
	m_min = min;
	m_max = max;
}

double CommandFitter::fit(const ValueLogger& learnedCmd, const ValueLogger& outsideTorques, const Trajectory& traj)
{
	Matrix<double, Dynamic, 4> A(learnedCmd.count(), 4);
	Matrix<double, Dynamic, 1> b(learnedCmd.count());

	int i = 0;
	for(ValueLogger::const_iterator it = learnedCmd.begin(); it.isValid(); ++it)
	{
		// HACK: Ignore the first 0.5s because the deviation is going to be
		// quite high here
		if(it.time() < 0.5)
			continue;

		if(it.value() < m_min || it.value() > m_max)
			continue;

		double time = it.time() + m_gen->latency();

		double pos = traj.position(time);
		double vel = traj.velocity(time);
		double acc = traj.acceleration(time);

		b(i) = (it.value() - pos) / m_gen->currentFactor();

		A.row(i) = m_gen->commandPartsFor(
			pos, vel, acc,
			outsideTorques.valueAtTime(time)
		).transpose();

		++i;
	}

	A = A.topRows(i);
	b = b.head(i);

	MatrixXd coefficients = A.jacobiSvd(ComputeFullU | ComputeFullV).solve(b);

	m_gen->setCoefficients(coefficients);

	// return sum of square errors
	return (A*coefficients - b).norm();
}

ext: cpp


text:
// Copyright (c) 2020 OUXT Polaris
//
// 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.

#ifndef LOCAL_WAYPOINT_SERVER__LOCAL_WAYPOINT_SERVER_COMPONENT_HPP_
#define LOCAL_WAYPOINT_SERVER__LOCAL_WAYPOINT_SERVER_COMPONENT_HPP_

#if __cplusplus
extern "C" {
#endif

// The below macros are taken from https://gcc.gnu.org/wiki/Visibility and from
// demos/composition/include/composition/visibility_control.h at https://github.com/ros2/demos
#if defined _WIN32 || defined __CYGWIN__
#ifdef __GNUC__
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_EXPORT __attribute__((dllexport))
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_IMPORT __attribute__((dllimport))
#else
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_EXPORT __declspec(dllexport)
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_IMPORT __declspec(dllimport)
#endif
#ifdef LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_BUILDING_DLL
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC \
  LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_EXPORT
#else
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC \
  LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_IMPORT
#endif
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC_TYPE \
  LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_LOCAL
#else
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_EXPORT \
  __attribute__((visibility("default")))
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_IMPORT
#if __GNUC__ >= 4
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC \
  __attribute__((visibility("default")))
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_LOCAL \
  __attribute__((visibility("hidden")))
#else
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_LOCAL
#endif
#define LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC_TYPE
#endif

#if __cplusplus
}  // extern "C"
#endif

#include <quaternion_operation/quaternion_operation.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>

#include <boost/optional.hpp>
#include <geometry_msgs/msg/pose_stamped.hpp>
#include <hermite_path_planner/hermite_path_generator.hpp>
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/laser_scan.hpp>
#include <string>
#include <vector>

namespace local_waypoint_server
{
class LocalWaypointServerComponent : public rclcpp::Node
{
public:
  LOCAL_WAYPOINT_SERVER_LOCAL_WAYPOINT_SERVER_COMPONENT_PUBLIC
  explicit LocalWaypointServerComponent(const rclcpp::NodeOptions & options);
  void GoalPoseCallback(const geometry_msgs::msg::PoseStamped::SharedPtr msg);

private:
  std::string goal_pose_topic_;
  rclcpp::Subscription<geometry_msgs::msg::PoseStamped>::SharedPtr goal_pose_sub_;
  boost::optional<geometry_msgs::msg::PoseStamped> goal_pose_;
  rclcpp::Publisher<geometry_msgs::msg::PoseStamped>::SharedPtr local_waypoint_pub_;
  void scanCallback(const sensor_msgs::msg::LaserScan::SharedPtr data);
  rclcpp::Subscription<sensor_msgs::msg::LaserScan>::SharedPtr scan_sub_;
  boost::optional<sensor_msgs::msg::LaserScan> scan_;
  std::vector<geometry_msgs::msg::Point> scan_points_;
  void currentPoseCallback(const geometry_msgs::msg::PoseStamped::SharedPtr data);
  rclcpp::Subscription<geometry_msgs::msg::PoseStamped>::SharedPtr current_pose_sub_;
  boost::optional<geometry_msgs::msg::PoseStamped> current_pose_;
  void updateLocalWaypoint();
  std::shared_ptr<hermite_path_planner::HermitePathGenerator> generator_;
  std::string planning_frame_id_;
  geometry_msgs::msg::PoseStamped TransformToPlanningFrame(geometry_msgs::msg::PoseStamped pose);
  geometry_msgs::msg::PointStamped TransformToPlanningFrame(geometry_msgs::msg::PointStamped pose);
  tf2_ros::Buffer buffer_;
  tf2_ros::TransformListener listener_;
  std::vector<geometry_msgs::msg::Point> getPoints(sensor_msgs::msg::LaserScan scan);
  bool checkCollision(geometry_msgs::msg::PoseStamped goal_pose, double & longitudinal_distance);
  double robot_width_;
  boost::optional<geometry_msgs::msg::PoseStamped> previous_local_waypoint_;
  bool isSame(geometry_msgs::msg::PoseStamped pose0, geometry_msgs::msg::PoseStamped pose1);
  rclcpp::Subscription<hermite_path_msgs::msg::HermitePathStamped>::SharedPtr hermite_path_sub_;
  void hermitePathCallback(const hermite_path_msgs::msg::HermitePathStamped::SharedPtr data);
  boost::optional<hermite_path_msgs::msg::HermitePathStamped> current_path_;
  boost::optional<double> checkCollisionToCurrentPath();
  boost::optional<double> checkCollisionToPath(hermite_path_msgs::msg::HermitePath path);
  std::vector<geometry_msgs::msg::Pose> getLocalWaypointCandidates(double obstacle_t);
  boost::optional<geometry_msgs::msg::Pose> evaluateCandidates(
    std::vector<geometry_msgs::msg::Pose> candidates);
  int num_candidates_;
  double sampling_interval_;
  double sampling_offset_;
  double margin_;
  boost::optional<geometry_msgs::msg::Pose> replaned_goalpose_;
  rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr marker_pub_;
  rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr marker_no_collision_pub_;
};
}  // namespace local_waypoint_server
#endif  // LOCAL_WAYPOINT_SERVER__LOCAL_WAYPOINT_SERVER_COMPONENT_HPP_

ext: hpp


text:
// Boost.TypeErasure library
//
// Copyright 2012 Steven Watanabe
//
// 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)
//
// $Id$

#ifndef BOOST_TYPE_ERASURE_IS_EMPTY_HPP_INCLUDED
#define BOOST_TYPE_ERASURE_IS_EMPTY_HPP_INCLUDED

#include <boost/type_erasure/detail/access.hpp>

namespace boost {
namespace type_erasure {

/** Returns true for an empty @ref any. */
template<class T>
bool is_empty(const T& arg) {
    return ::boost::type_erasure::detail::access::data(arg).data == 0;
}

}
}

#endif


text:
#ifndef BUILD_SRC_INCLUDE_TYPES_HPP_
#define BUILD_SRC_INCLUDE_TYPES_HPP_

#include <iostream>
#include <mutex>
#include <frame.hpp>
#include <mutex>
#include <deque>
#include <opencv4/opencv2/highgui/highgui.hpp>
#include <Eigen/Dense>

namespace MonocularVO
{
using FrameSharedPtr = std::shared_ptr<Frame>;

using FrameSharedPtr = std::shared_ptr<Frame>;
struct Observation
{
  bool is_ref_frame{};
  bool is_keyframe;
  int id_frame;
  cv::Vec6d pose;
  cv::Mat mat_pose_4x4;

  int id_point3d;
  cv::Point3d point3d;

  int id_point2d{};
  cv::Point2d point2d;

  bool is_optimized;
  bool is_initialized;

  int id_obs;

  Observation(
      const int &id_frame, const int &id_point3d,
      const cv::Vec6d &pose,  const cv::Mat &mat_pose_4x4, const cv::Point2d &point2d,
      const cv::Point3d &point3d, const bool &is_keyframe, const bool& is_ref_frame,
      const bool& is_optimized, const bool& is_initialized)
  {
    this->id_frame = id_frame;
    this->pose = pose;
    this->mat_pose_4x4 = mat_pose_4x4;
    this->is_keyframe = is_keyframe;
    this->is_ref_frame;
    this->id_point3d = id_point3d;
    this->point3d = point3d;
    this->point2d = point2d;
    this->is_optimized = is_optimized;
    this->is_initialized = is_initialized;
  }
};

typedef std::shared_ptr<Observation> ObservationSharedPtr;

struct LocalObservations
{
 public:
  std::vector<cv::Vec6d> camera_poses;
  std::vector<std::vector<cv::Point2d>> points2D;
  std::vector<cv::Point3d> points3D;
};

class MatchKeyFrame
{
public:
  using MatchKeyFrameSharedPtr = std::shared_ptr<MatchKeyFrame>;

  int old_frame_id;
  std::vector<cv::KeyPoint> old_keyframe_kpts;
  std::vector<int> old_kpt_ids;
  int new_frame_id;
  std::vector<cv::KeyPoint> new_keyframe_kpts;
  std::vector<int> new_kpt_ids;
  //cv::Mat img_concat;

  MatchKeyFrame(
      const int& old_frame_id,
      const std::vector<cv::KeyPoint>& old_keyframe_kpts,
      const std::vector<int>& old_kpt_ids,
      const int& new_frame_id,
      const std::vector<cv::KeyPoint>& new_keyframe_kpts,
      const std::vector<int>& new_kpt_ids
      //const cv::Mat& img_concat
      )
  {
    this->old_frame_id = old_frame_id;
    this->old_keyframe_kpts = old_keyframe_kpts;
    this->old_kpt_ids = old_kpt_ids;
    this->new_frame_id = new_frame_id;
    this->new_keyframe_kpts = new_keyframe_kpts;
    this->new_kpt_ids = new_kpt_ids;
    //this->img_concat = img_concat;
  }
};

}

#endif  // BUILD_SRC_INCLUDE_TYPES_HPP_

max_stars_repo_name: goktugyildirim/MonocularVisualOdometry

max_issues_repo_name: goktugyildirim/MonocularVisualOdometry

max_forks_repo_name: goktugyildirim/MonocularVisualOdometry


text:
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James
// 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_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <boost/unordered/detail/util.hpp>
#include <boost/unordered/detail/allocator_helpers.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/swap.hpp>
#include <boost/assert.hpp>
#include <boost/limits.hpp>
#include <boost/iterator.hpp>

#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4127) // conditional expression is constant
#endif

namespace boost { namespace unordered { namespace detail {

    template <typename Types> struct table;
    template <typename NodePointer> struct bucket;
    struct ptr_bucket;
    template <typename A, typename Bucket, typename Node, typename Policy>
    struct buckets;
    template <typename Types> struct table_impl;
    template <typename Types> struct grouped_table_impl;

    ///////////////////////////////////////////////////////////////////
    //
    // Node construction

    template <typename NodeAlloc>
    struct node_constructor
    {
    private:

        typedef NodeAlloc node_allocator;
        typedef boost::unordered::detail::allocator_traits<NodeAlloc>
            node_allocator_traits;
        typedef typename node_allocator_traits::value_type node;
        typedef typename node_allocator_traits::pointer node_pointer;
        typedef typename node::value_type value_type;

        node_allocator& alloc_;
        node_pointer node_;
        bool constructed_;

    public:

        node_constructor(node_allocator& n) :
            alloc_(n),
            node_(),
            constructed_(false)
        {
        }

        ~node_constructor();

        void construct_node();

        template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
        void construct_value(BOOST_UNORDERED_EMPLACE_ARGS)
        {
            BOOST_ASSERT(node_ && !constructed_);
            boost::unordered::detail::construct_node(alloc_,
                boost::addressof(*node_), BOOST_UNORDERED_EMPLACE_FORWARD);
            node_->init(static_cast<typename node::link_pointer>(node_));
            constructed_ = true;
        }

        template <typename A0>
        void construct_value2(BOOST_FWD_REF(A0) a0)
        {
            BOOST_ASSERT(node_ && !constructed_);
#   if defined(BOOST_UNORDERED_VARIADIC_MOVE)
            boost::unordered::detail::construct_node(alloc_,
                boost::addressof(*node_), boost::forward<A0>(a0));
#   else
            boost::unordered::detail::construct_node(alloc_,
                boost::addressof(*node_),
                boost::unordered::detail::create_emplace_args(
                    boost::forward<A0>(a0)));
#   endif
            constructed_ = true;
            node_->init(static_cast<typename node::link_pointer>(node_));
        }

        value_type const& value() const {
            BOOST_ASSERT(node_ && constructed_);
            return node_->value();
        }

        node_pointer get()
        {
            return node_;
        }

        // no throw
        node_pointer release()
        {
            node_pointer p = node_;
            node_ = node_pointer();
            return p;
        }

    private:
        node_constructor(node_constructor const&);
        node_constructor& operator=(node_constructor const&);
    };

    template <typename Alloc>
    node_constructor<Alloc>::~node_constructor()
    {
        if (node_) {
            if (constructed_) {
                boost::unordered::detail::destroy_node(alloc_,
                    boost::addressof(*node_));
            }

            node_allocator_traits::deallocate(alloc_, node_, 1);
        }
    }

    template <typename Alloc>
    void node_constructor<Alloc>::construct_node()
    {
        if(!node_) {
            constructed_ = false;
            node_ = node_allocator_traits::allocate(alloc_, 1);
        }
        else if (constructed_) {
            boost::unordered::detail::destroy_node(alloc_,
                boost::addressof(*node_));
            constructed_ = false;
        }
    }

    ///////////////////////////////////////////////////////////////////
    //
    // Bucket

    template <typename NodePointer>
    struct bucket
    {
        typedef NodePointer previous_pointer;
        previous_pointer next_;

        bucket() : next_() {}

        previous_pointer first_from_start()
        {
            return next_;
        }

        enum { extra_node = true };
    };

    struct ptr_bucket
    {
        typedef ptr_bucket* previous_pointer;
        previous_pointer next_;

        ptr_bucket() : next_(0) {}

        previous_pointer first_from_start()
        {
            return this;
        }

        enum { extra_node = false };
    };

    template <typename LinkPointer>
    struct node_base
    {
        typedef LinkPointer link_pointer;
        link_pointer next_;

        node_base() : next_() {}
    };

}}}

namespace boost { namespace unordered { namespace iterator_detail {

    ////////////////////////////////////////////////////////////////////////////
    // Iterators
    //
    // all no throw

    template <typename NodePointer, typename Value> struct iterator;
    template <typename ConstNodePointer, typename NodePointer,
        typename Value> struct c_iterator;
    template <typename NodePointer, typename Value, typename Policy>
        struct l_iterator;
    template <typename ConstNodePointer, typename NodePointer,
        typename Value, typename Policy> struct cl_iterator;

    // Local Iterators
    //
    // all no throw

    template <typename NodePointer, typename Value, typename Policy>
    struct l_iterator
        : public boost::iterator<
            std::forward_iterator_tag, Value, std::ptrdiff_t,
            NodePointer, Value&>
    {
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
        template <typename ConstNodePointer, typename NodePointer2,
                typename Value2, typename Policy2>
        friend struct boost::unordered::iterator_detail::cl_iterator;
    private:
#endif
        typedef NodePointer node_pointer;
        typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
            iterator;
        node_pointer ptr_;
        std::size_t bucket_;
        std::size_t bucket_count_;

    public:

        l_iterator() : ptr_() {}

        l_iterator(iterator x, std::size_t b, std::size_t c)
            : ptr_(x.node_), bucket_(b), bucket_count_(c) {}

        Value& operator*() const {
            return ptr_->value();
        }

        Value* operator->() const {
            return ptr_->value_ptr();
        }

        l_iterator& operator++() {
            ptr_ = static_cast<node_pointer>(ptr_->next_);
            if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_)
                    != bucket_)
                ptr_ = node_pointer();
            return *this;
        }

        l_iterator operator++(int) {
            l_iterator tmp(*this);
            ++(*this);
            return tmp;
        }

        bool operator==(l_iterator x) const {
            return ptr_ == x.ptr_;
        }

        bool operator!=(l_iterator x) const {
            return ptr_ != x.ptr_;
        }
    };

    template <typename ConstNodePointer, typename NodePointer, typename Value,
             typename Policy>
    struct cl_iterator
        : public boost::iterator<
            std::forward_iterator_tag, Value, std::ptrdiff_t,
            ConstNodePointer, Value const&>
    {
        friend struct boost::unordered::iterator_detail::l_iterator
            <NodePointer, Value, Policy>;
    private:

        typedef NodePointer node_pointer;
        typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
            iterator;
        node_pointer ptr_;
        std::size_t bucket_;
        std::size_t bucket_count_;

    public:

        cl_iterator() : ptr_() {}

        cl_iterator(iterator x, std::size_t b, std::size_t c) :
            ptr_(x.node_), bucket_(b), bucket_count_(c) {}

        cl_iterator(boost::unordered::iterator_detail::l_iterator<
                NodePointer, Value, Policy> const& x) :
            ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_)
        {}

        Value const&
            operator*() const {
            return ptr_->value();
        }

        Value const* operator->() const {
            return ptr_->value_ptr();
        }

        cl_iterator& operator++() {
            ptr_ = static_cast<node_pointer>(ptr_->next_);
            if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_)
                    != bucket_)
                ptr_ = node_pointer();
            return *this;
        }

        cl_iterator operator++(int) {
            cl_iterator tmp(*this);
            ++(*this);
            return tmp;
        }

        friend bool operator==(cl_iterator const& x, cl_iterator const& y) {
            return x.ptr_ == y.ptr_;
        }

        friend bool operator!=(cl_iterator const& x, cl_iterator const& y) {
            return x.ptr_ != y.ptr_;
        }
    };

    template <typename NodePointer, typename Value>
    struct iterator
        : public boost::iterator<
            std::forward_iterator_tag, Value, std::ptrdiff_t,
            NodePointer, Value&>
    {
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
        template <typename, typename, typename>
        friend struct boost::unordered::iterator_detail::c_iterator;
        template <typename, typename, typename>
        friend struct boost::unordered::iterator_detail::l_iterator;
        template <typename, typename, typename, typename>
        friend struct boost::unordered::iterator_detail::cl_iterator;
        template <typename>
        friend struct boost::unordered::detail::table;
        template <typename, typename, typename, typename>
        friend struct boost::unordered::detail::buckets;
        template <typename>
        friend struct boost::unordered::detail::table_impl;
        template <typename>
        friend struct boost::unordered::detail::grouped_table_impl;
    private:
#endif
        typedef NodePointer node_pointer;
        node_pointer node_;

    public:

        iterator() : node_() {}

        explicit iterator(node_pointer const& x) : node_(x) {}

        Value& operator*() const {
            return node_->value();
        }

        Value* operator->() const {
            return &node_->value();
        }

        iterator& operator++() {
            node_ = static_cast<node_pointer>(node_->next_);
            return *this;
        }

        iterator operator++(int) {
            iterator tmp(node_);
            node_ = static_cast<node_pointer>(node_->next_);
            return tmp;
        }

        bool operator==(iterator const& x) const {
            return node_ == x.node_;
        }

        bool operator!=(iterator const& x) const {
            return node_ != x.node_;
        }
    };

    template <typename ConstNodePointer, typename NodePointer, typename Value>
    struct c_iterator
        : public boost::iterator<
            std::forward_iterator_tag, Value, std::ptrdiff_t,
            ConstNodePointer, Value const&>
    {
        friend struct boost::unordered::iterator_detail::iterator<
                NodePointer, Value>;

#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
        template <typename>
        friend struct boost::unordered::detail::table;
        template <typename, typename, typename, typename>
        friend struct boost::unordered::detail::buckets;
        template <typename>
        friend struct boost::unordered::detail::table_impl;
        template <typename>
        friend struct boost::unordered::detail::grouped_table_impl;

    private:
#endif

        typedef NodePointer node_pointer;
        typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
            iterator;
        node_pointer node_;

    public:

        c_iterator() : node_() {}

        explicit c_iterator(node_pointer const& x) : node_(x) {}

        c_iterator(boost::unordered::iterator_detail::iterator<
                NodePointer, Value> const& x) : node_(x.node_) {}

        Value const& operator*() const {
            return node_->value();
        }

        Value const* operator->() const {
            return &node_->value();
        }

        c_iterator& operator++() {
            node_ = static_cast<node_pointer>(node_->next_);
            return *this;
        }

        c_iterator operator++(int) {
            c_iterator tmp(node_);
            node_ = static_cast<node_pointer>(node_->next_);
            return tmp;
        }

        friend bool operator==(c_iterator const& x, c_iterator const& y) {
            return x.node_ == y.node_;
        }

        friend bool operator!=(c_iterator const& x, c_iterator const& y) {
            return x.node_ != y.node_;
        }
    };
}}}

namespace boost { namespace unordered { namespace detail {

    ///////////////////////////////////////////////////////////////////
    //
    // Hash Policy
    //
    // Don't really want buckets to derive from this, but will for now.

    template <typename SizeT>
    struct prime_policy
    {
        template <typename Hash, typename T>
        static inline SizeT apply_hash(Hash const& hf, T const& x) {
            return hf(x);
        }

        static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) {
            return hash % bucket_count;
        }

        static inline SizeT new_bucket_count(SizeT min) {
            return boost::unordered::detail::next_prime(min);
        }

        static inline SizeT prev_bucket_count(SizeT max) {
            return boost::unordered::detail::prev_prime(max);
        }
    };

    template <typename SizeT>
    struct mix64_policy
    {
        template <typename Hash, typename T>
        static inline SizeT apply_hash(Hash const& hf, T const& x) {
            SizeT key = hf(x);
            key = (~key) + (key << 21); // key = (key << 21) - key - 1;
            key = key ^ (key >> 24);
            key = (key + (key << 3)) + (key << 8); // key * 265
            key = key ^ (key >> 14);
            key = (key + (key << 2)) + (key << 4); // key * 21
            key = key ^ (key >> 28);
            key = key + (key << 31);
            return key;
        }

        static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) {
            return hash & (bucket_count - 1);
        }

        static inline SizeT new_bucket_count(SizeT min) {
            if (min <= 4) return 4;
            --min;
            min |= min >> 1;
            min |= min >> 2;
            min |= min >> 4;
            min |= min >> 8;
            min |= min >> 16;
            min |= min >> 32;
            return min + 1;
        }

        static inline SizeT prev_bucket_count(SizeT max) {
            max |= max >> 1;
            max |= max >> 2;
            max |= max >> 4;
            max |= max >> 8;
            max |= max >> 16;
            max |= max >> 32;
            return (max >> 1) + 1;
        }
    };

    template <int digits, int radix>
    struct pick_policy_impl {
        typedef prime_policy<std::size_t> type;
    };

    template <>
    struct pick_policy_impl<64, 2> {
        typedef mix64_policy<std::size_t> type;
    };

    struct pick_policy :
        pick_policy_impl<
            std::numeric_limits<std::size_t>::digits,
            std::numeric_limits<std::size_t>::radix> {};

    ///////////////////////////////////////////////////////////////////
    //
    // Buckets

    template <typename A, typename Bucket, typename Node, typename Policy>
    struct buckets : Policy
    {
    private:
        buckets(buckets const&);
        buckets& operator=(buckets const&);
    public:
        typedef boost::unordered::detail::allocator_traits<A> traits;
        typedef typename traits::value_type value_type;

        typedef Policy policy;
        typedef Node node;
        typedef Bucket bucket;
        typedef typename boost::unordered::detail::rebind_wrap<A, node>::type
            node_allocator;
        typedef typename boost::unordered::detail::rebind_wrap<A, bucket>::type
            bucket_allocator;
        typedef boost::unordered::detail::allocator_traits<node_allocator>
            node_allocator_traits;
        typedef boost::unordered::detail::allocator_traits<bucket_allocator>
            bucket_allocator_traits;
        typedef typename node_allocator_traits::pointer
            node_pointer;
        typedef typename node_allocator_traits::const_pointer
            const_node_pointer;
        typedef typename bucket_allocator_traits::pointer
            bucket_pointer;
        typedef typename bucket::previous_pointer
            previous_pointer;
        typedef boost::unordered::detail::node_constructor<node_allocator>
            node_constructor;

        typedef boost::unordered::iterator_detail::
            iterator<node_pointer, value_type> iterator;
        typedef boost::unordered::iterator_detail::
            c_iterator<const_node_pointer, node_pointer, value_type> c_iterator;
        typedef boost::unordered::iterator_detail::
            l_iterator<node_pointer, value_type, policy> l_iterator;
        typedef boost::unordered::iterator_detail::
            cl_iterator<const_node_pointer, node_pointer, value_type, policy>
            cl_iterator;

        // Members

        bucket_pointer buckets_;
        std::size_t bucket_count_;
        std::size_t size_;
        boost::unordered::detail::compressed<bucket_allocator, node_allocator>
            allocators_;

        // Data access

        bucket_allocator const& bucket_alloc() const
        {
            return allocators_.first();
        }

        node_allocator const& node_alloc() const
        {
            return allocators_.second();
        }

        bucket_allocator& bucket_alloc()
        {
            return allocators_.first();
        }

        node_allocator& node_alloc()
        {
            return allocators_.second();
        }

        std::size_t max_bucket_count() const
        {
            // -1 to account for the start bucket.
            return policy::prev_bucket_count(
                bucket_allocator_traits::max_size(bucket_alloc()) - 1);
        }

        bucket_pointer get_bucket(std::size_t bucket_index) const
        {
            return buckets_ + static_cast<std::ptrdiff_t>(bucket_index);
        }

        previous_pointer get_previous_start() const
        {
            return this->get_bucket(this->bucket_count_)->first_from_start();
        }

        previous_pointer get_previous_start(std::size_t bucket_index) const
        {
            return this->get_bucket(bucket_index)->next_;
        }

        iterator get_start() const
        {
            return iterator(static_cast<node_pointer>(
                        this->get_previous_start()->next_));
        }

        iterator get_start(std::size_t bucket_index) const
        {
            previous_pointer prev = this->get_previous_start(bucket_index);
            return prev ? iterator(static_cast<node_pointer>(prev->next_)) :
                iterator();
        }

        float load_factor() const
        {
            BOOST_ASSERT(this->bucket_count_ != 0);
            return static_cast<float>(this->size_)
                / static_cast<float>(this->bucket_count_);
        }

        std::size_t bucket_size(std::size_t index) const
        {
            if (!this->size_) return 0;
            iterator it = this->get_start(index);
            if (!it.node_) return 0;

            std::size_t count = 0;
            while(it.node_ && policy::to_bucket(
                        this->bucket_count_, it.node_->hash_) == index)
            {
                ++count;
                ++it;
            }

            return count;
        }

        ////////////////////////////////////////////////////////////////////////
        // Constructors

        buckets(node_allocator const& a, std::size_t bucket_count) :
            buckets_(),
            bucket_count_(bucket_count),
            size_(),
            allocators_(a,a)
        {
        }

        buckets(buckets& b, boost::unordered::detail::move_tag m) :
            buckets_(),
            bucket_count_(b.bucket_count_),
            size_(),
            allocators_(b.allocators_, m)
        {
            swap(b);
        }

        template <typename Types>
        buckets(boost::unordered::detail::table<Types>& x,
                boost::unordered::detail::move_tag m) :
            buckets_(),
            bucket_count_(x.bucket_count_),
            size_(),
            allocators_(x.allocators_, m)
        {
            swap(x);
        }

        ////////////////////////////////////////////////////////////////////////
        // Create buckets
        // (never called in constructor to avoid exception issues)

        void create_buckets()
        {
            boost::unordered::detail::array_constructor<bucket_allocator>
                constructor(bucket_alloc());

            // Creates an extra bucket to act as the start node.
            constructor.construct(bucket(), this->bucket_count_ + 1);

            if (bucket::extra_node)
            {
                node_constructor a(this->node_alloc());
                a.construct_node();

                // Since this node is just to mark the beginning it doesn't
                // contain a value, so just construct node::node_base
                // which containers the pointer to the next element.
                node_allocator_traits::construct(node_alloc(),
                    static_cast<typename node::node_base*>(
                        boost::addressof(*a.get())),
                    typename node::node_base());

                (constructor.get() +
                    static_cast<std::ptrdiff_t>(this->bucket_count_))->next_ =
                        a.release();
            }

            this->buckets_ = constructor.release();
        }

        ////////////////////////////////////////////////////////////////////////
        // Swap and Move

        void swap(buckets& other, false_type = false_type())
        {
            BOOST_ASSERT(node_alloc() == other.node_alloc());
            boost::swap(buckets_, other.buckets_);
            boost::swap(bucket_count_, other.bucket_count_);
            boost::swap(size_, other.size_);
        }

        void swap(buckets& other, true_type)
        {
            allocators_.swap(other.allocators_);
            boost::swap(buckets_, other.buckets_);
            boost::swap(bucket_count_, other.bucket_count_);
            boost::swap(size_, other.size_);
        }

        void move_buckets_from(buckets& other)
        {
            BOOST_ASSERT(node_alloc() == other.node_alloc());
            BOOST_ASSERT(!this->buckets_);
            this->buckets_ = other.buckets_;
            this->bucket_count_ = other.bucket_count_;
            this->size_ = other.size_;
            other.buckets_ = bucket_pointer();
            other.bucket_count_ = 0;
            other.size_ = 0;
        }

        ////////////////////////////////////////////////////////////////////////
        // Delete/destruct

        inline void delete_node(c_iterator n)
        {
            boost::unordered::detail::destroy_node(
                node_alloc(), boost::addressof(*n.node_));
            node_allocator_traits::deallocate(node_alloc(), n.node_, 1);
            --size_;
        }

        std::size_t delete_nodes(c_iterator begin, c_iterator end)
        {
            std::size_t count = 0;

            while(begin != end) {
                c_iterator n = begin;
                ++begin;
                delete_node(n);
                ++count;
            }

            return count;
        }

        inline void delete_extra_node(bucket_pointer) {}

        inline void delete_extra_node(node_pointer n) {
            node_allocator_traits::destroy(node_alloc(),
                static_cast<typename node::node_base*>(boost::addressof(*n)));
            node_allocator_traits::deallocate(node_alloc(), n, 1);
        }

        inline ~buckets()
        {
            this->delete_buckets();
        }

        void delete_buckets()
        {
            if(this->buckets_) {
                previous_pointer prev = this->get_previous_start();

                while(prev->next_) {
                    node_pointer n = static_cast<node_pointer>(prev->next_);
                    prev->next_ = n->next_;
                    delete_node(iterator(n));
                }

                delete_extra_node(prev);

                bucket_pointer end = this->get_bucket(this->bucket_count_ + 1);
                for(bucket_pointer it = this->buckets_; it != end; ++it)
                {
                    bucket_allocator_traits::destroy(bucket_alloc(),
                        boost::addressof(*it));
                }

                bucket_allocator_traits::deallocate(bucket_alloc(),
                    this->buckets_, this->bucket_count_ + 1);

                this->buckets_ = bucket_pointer();
            }

            BOOST_ASSERT(!this->size_);
        }

        void clear()
        {
            if(!this->size_) return;

            previous_pointer prev = this->get_previous_start();

            while(prev->next_) {
                node_pointer n = static_cast<node_pointer>(prev->next_);
                prev->next_ = n->next_;
                delete_node(iterator(n));
            }

            bucket_pointer end = this->get_bucket(this->bucket_count_);
            for(bucket_pointer it = this->buckets_; it != end; ++it)
            {
                it->next_ = node_pointer();
            }

            BOOST_ASSERT(!this->size_);
        }

        // This is called after erasing a node or group of nodes to fix up
        // the bucket pointers.
        void fix_buckets(bucket_pointer this_bucket,
                previous_pointer prev, node_pointer next)
        {
            if (!next)
            {
                if (this_bucket->next_ == prev)
                    this_bucket->next_ = node_pointer();
            }
            else
            {
                bucket_pointer next_bucket = this->get_bucket(
                    policy::to_bucket(this->bucket_count_, next->hash_));

                if (next_bucket != this_bucket)
                {
                    next_bucket->next_ = prev;
                    if (this_bucket->next_ == prev)
                        this_bucket->next_ = node_pointer();
                }
            }
        }

        // This is called after erasing a range of nodes to fix any bucket
        // pointers into that range.
        void fix_buckets_range(std::size_t bucket_index,
                previous_pointer prev, node_pointer begin, node_pointer end)
        {
            node_pointer n = begin;

            // If we're not at the start of the current bucket, then
            // go to the start of the next bucket.
            if (this->get_bucket(bucket_index)->next_ != prev)
            {
                for(;;) {
                    n = static_cast<node_pointer>(n->next_);
                    if (n == end) return;

                    std::size_t new_bucket_index =
                        policy::to_bucket(this->bucket_count_, n->hash_);
                    if (bucket_index != new_bucket_index) {
                        bucket_index = new_bucket_index;
                        break;
                    }
                }
            }

            // Iterate through the remaining nodes, clearing out the bucket
            // pointers.
            this->get_bucket(bucket_index)->next_ = previous_pointer();
            for(;;) {
                n = static_cast<node_pointer>(n->next_);
                if (n == end) break;

                std::size_t new_bucket_index =
                    policy::to_bucket(this->bucket_count_, n->hash_);
                if (bucket_index != new_bucket_index) {
                    bucket_index = new_bucket_index;
                    this->get_bucket(bucket_index)->next_ = previous_pointer();
                }
            };

            // Finally fix the bucket containing the trailing node.
            if (n) {
                this->get_bucket(
                    policy::to_bucket(this->bucket_count_, n->hash_))->next_
                    = prev;
            }
        }
    };

    ////////////////////////////////////////////////////////////////////////////
    // Functions

    // Assigning and swapping the equality and hash function objects
    // needs strong exception safety. To implement that normally we'd
    // require one of them to be known to not throw and the other to
    // guarantee strong exception safety. Unfortunately they both only
    // have basic exception safety. So to acheive strong exception
    // safety we have storage space for two copies, and assign the new
    // copies to the unused space. Then switch to using that to use
    // them. This is implemented in 'set_hash_functions' which
    // atomically assigns the new function objects in a strongly
    // exception safe manner.

    template <class H, class P> class set_hash_functions;

    template <class H, class P>
    class functions
    {
        friend class boost::unordered::detail::set_hash_functions<H, P>;
        functions& operator=(functions const&);

        typedef compressed<H, P> function_pair;

        typedef typename boost::aligned_storage<
            sizeof(function_pair),
            boost::alignment_of<function_pair>::value>::type aligned_function;

        bool current_; // The currently active functions.
        aligned_function funcs_[2];

        function_pair const& current() const {
            return *static_cast<function_pair const*>(
                static_cast<void const*>(&funcs_[current_]));
        }

        void construct(bool which, H const& hf, P const& eq)
        {
            new((void*) &funcs_[which]) function_pair(hf, eq);
        }

        void construct(bool which, function_pair const& f)
        {
            new((void*) &funcs_[which]) function_pair(f);
        }

        void destroy(bool which)
        {
            boost::unordered::detail::destroy((function_pair*)(&funcs_[which]));
        }

    public:

        functions(H const& hf, P const& eq)
            : current_(false)
        {
            construct(current_, hf, eq);
        }

        functions(functions const& bf)
            : current_(false)
        {
            construct(current_, bf.current());
        }

        ~functions() {
            this->destroy(current_);
        }

        H const& hash_function() const {
            return current().first();
        }

        P const& key_eq() const {
            return current().second();
        }
    };

    template <class H, class P>
    class set_hash_functions
    {
        set_hash_functions(set_hash_functions const&);
        set_hash_functions& operator=(set_hash_functions const&);

        functions<H,P>& functions_;
        bool tmp_functions_;

    public:

        set_hash_functions(functions<H,P>& f, H const& h, P const& p)
          : functions_(f),
            tmp_functions_(!f.current_)
        {
            f.construct(tmp_functions_, h, p);
        }

        set_hash_functions(functions<H,P>& f, functions<H,P> const& other)
          : functions_(f),
            tmp_functions_(!f.current_)
        {
            f.construct(tmp_functions_, other.current());
        }

        ~set_hash_functions()
        {
            functions_.destroy(tmp_functions_);
        }

        void commit()
        {
            functions_.current_ = tmp_functions_;
            tmp_functions_ = !tmp_functions_;
        }
    };
}}}

#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif

#endif


text:
/**
 ** Isaac Genome Alignment Software
 ** Copyright (c) 2010-2017 Illumina, Inc.
 ** All rights reserved.
 **
 ** This software is provided under the terms and conditions of the
 ** GNU GENERAL PUBLIC LICENSE Version 3
 **
 ** You should have received a copy of the GNU GENERAL PUBLIC LICENSE Version 3
 ** along with this program. If not, see
 ** <https://github.com/illumina/licenses/>.
 **
 ** \file BoostArchiveHelpers.hh
 **
 ** \brief helpers for serializing various basic types
 **
 ** \author Roman Petrovski
 **/

#ifndef ISAAC_COMMON_BOOST_ARCHIVE_HELPERS_H
#define ISAAC_COMMON_BOOST_ARCHIVE_HELPERS_H

#include <boost/serialization/split_free.hpp>

#include "workflow/AlignWorkflow.hh"

BOOST_SERIALIZATION_SPLIT_FREE(boost::filesystem::path)

/**
 * \brief serialization implementation types that don't require private member access
 * can be placed in the boost::serialization namespace
 */
namespace boost {
namespace serialization {

template <class Archive>
void save(Archive &ar, const boost::filesystem::path &p, const unsigned int version)
{
    ar << boost::serialization::make_nvp("path", p.string());
}

template <class Archive>
void load(Archive &ar, boost::filesystem::path &p, const unsigned int version)
{
    std::string tmp;
    ar >> boost::serialization::make_nvp("path", tmp);
    p = tmp;
}

template <class Archive>
void serialize(Archive &ar, std::pair<std::string, std::string> &ps, const unsigned int version)
{
    ar & BOOST_SERIALIZATION_NVP(ps.first);
    ar & BOOST_SERIALIZATION_NVP(ps.second);
}

} //namespace serialization
} //namespace boost

#endif //ISAAC_COMMON_BOOST_ARCHIVE_HELPERS_H

ext: hh


text:
#pragma once

#include <boost/lexical_cast.hpp>
#include <functional>
#include <string>
#include <type_traits>

#include "types.hpp"
#include "utils/assert.hpp"

namespace opossum {

// source: http://stackoverflow.com/questions/16893992/check-if-type-can-be-explicitly-converted
template <class From, class To>
struct is_explicitly_convertible {
  enum { value = std::is_constructible<To, From>::value && !std::is_convertible<From, To>::value };
};

// source: http://stackoverflow.com/questions/27709461/check-if-type-can-be-an-argument-to-boostlexical-caststring
template <typename T, typename = void>
struct IsLexCastable : std::false_type {};

template <typename T>
struct IsLexCastable<T, decltype(void(std::declval<std::ostream&>() << std::declval<T>()))> : std::true_type {};

/* EQUAL */
// L and R are implicitly convertible
template <typename L, typename R>
typename std::enable_if<std::is_convertible<L, R>::value && std::is_convertible<R, L>::value, bool>::type value_equal(
    L l, R r) {
  return l == r;
}

// L is arithmetic, R is explicitly convertible to L
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<L>::value && IsLexCastable<R>::value && !std::is_arithmetic<R>::value,
                        bool>::type
value_equal(L l, R r) {
  return boost::lexical_cast<L>(r) == l;
}

// R is arithmetic, L is explicitly convertible to R
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<R>::value && IsLexCastable<L>::value && !std::is_arithmetic<L>::value,
                        bool>::type
value_equal(L l, R r) {
  return boost::lexical_cast<R>(l) == r;
}

/* SMALLER */
// L and R are implicitly convertible
template <typename L, typename R>
typename std::enable_if<std::is_convertible<L, R>::value && std::is_convertible<R, L>::value, bool>::type value_smaller(
    L l, R r) {
  return l < r;
}

// L is arithmetic, R is explicitly convertible to L
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<L>::value && IsLexCastable<R>::value && !std::is_arithmetic<R>::value,
                        bool>::type
value_smaller(L l, R r) {
  return boost::lexical_cast<L>(r) < l;
}

// R is arithmetic, L is explicitly convertible to R
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<R>::value && IsLexCastable<L>::value && !std::is_arithmetic<L>::value,
                        bool>::type
value_smaller(L l, R r) {
  return boost::lexical_cast<R>(l) < r;
}

/* GREATER > */
// L and R are implicitly convertible
template <typename L, typename R>
typename std::enable_if<std::is_convertible<L, R>::value && std::is_convertible<R, L>::value, bool>::type value_greater(
    L l, R r) {
  return l > r;
}

// L is arithmetic, R is explicitly convertible to L
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<L>::value && IsLexCastable<R>::value && !std::is_arithmetic<R>::value,
                        bool>::type
value_greater(L l, R r) {
  return boost::lexical_cast<L>(r) > l;
}

// R is arithmetic, L is explicitly convertible to R
template <typename L, typename R>
typename std::enable_if<std::is_arithmetic<R>::value && IsLexCastable<L>::value && !std::is_arithmetic<L>::value,
                        bool>::type
value_greater(L l, R r) {
  return boost::lexical_cast<R>(l) > r;
}

// Function that calls a given functor with the correct std comparator
template <typename Functor>
void with_comparator(const PredicateCondition predicate_condition, const Functor& func) {
  switch (predicate_condition) {
    case PredicateCondition::Equals:
      return func(std::equal_to<void>{});

    case PredicateCondition::NotEquals:
      return func(std::not_equal_to<void>{});

    case PredicateCondition::LessThan:
      return func(std::less<void>{});

    case PredicateCondition::LessThanEquals:
      return func(std::less_equal<void>{});

    case PredicateCondition::GreaterThan:
      return func(std::greater<void>{});

    case PredicateCondition::GreaterThanEquals:
      return func(std::greater_equal<void>{});

    default:
      Fail("Unsupported operator.");
  }
}

}  // namespace opossum

max_stars_repo_name: IanJamesMcKay/InMemoryDB

max_issues_repo_name: IanJamesMcKay/InMemoryDB

max_forks_repo_name: IanJamesMcKay/InMemoryDB


text:
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// 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 <config.h>
#include <lease_cmds.h>
#include <config/command_mgr.h>
#include <lease_parser.h>
#include <cc/command_interpreter.h>
#include <cc/data.h>
#include <asiolink/io_address.h>
#include <lease_cmds_log.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcp/duid.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
#include <exceptions/exceptions.h>
#include <boost/bind.hpp>
#include <string>

using namespace isc::dhcp;
using namespace isc::data;
using namespace isc::config;
using namespace isc::asiolink;
using namespace std;

namespace isc {
namespace lease_cmds {

/// @brief Wrapper class around reservation command handlers.
class LeaseCmdsImpl {
public:
    LeaseCmdsImpl();

    ~LeaseCmdsImpl();

/// @brief Parameters specified for reservation-get and reservation-del
///
/// As both call types (get and delete) need specify which reservation to
/// act on, they have the same set of parameters. In particular, those
/// two call types support the following sets of parameters:
/// - address
/// - subnet-id, identifier-type, identifier-value (v4)
/// - subnet-id, lease-type, iaid, identifier-type, identifier-value (v6)
///
/// This class stores those parameters and is used to pass them around.
class Parameters {
public:

    /// @brief specifies type of query (by IP addr, by hwaddr, by DUID)
    typedef enum {
        TYPE_ADDR,    ///< query by IP address (either v4 or v6)
        TYPE_HWADDR,  ///< query by hardware address (v4 only)
        TYPE_DUID     ///< query by DUID (v6 only)
    } Type;

    /// @brief Specifies subnet-id (always used)
    SubnetID subnet_id;

    /// @brief Specifies IPv4/v6 address (used when query_type is TYPE_ADDR)
    IOAddress addr;

    /// @brief Specifies hardware address (used when query_type is TYPE_HWADDR)
    HWAddrPtr hwaddr;

    /// @brief Specifies identifier value (used when query_type is TYPE_DUID)
    isc::dhcp::DuidPtr duid;

    /// @brief Attempts to covert text to one of specified types
    ///
    /// Supported values are: "address", hw-address and duid.
    ///
    /// @param txt text to be converted
    /// @return value converted to Parameters::Type
    /// @throw BadValue if unsupported type is specified
    static Type txtToType(const std::string& txt) {
        if (txt == "address") {
            return (Parameters::TYPE_ADDR);
        } else if (txt == "hw-address") {
            return (Parameters::TYPE_HWADDR);
        } else if (txt == "duid") {
            return (Parameters::TYPE_DUID);
        } else {
            isc_throw(BadValue, "Incorrect identifier type: "
                      << txt << ", the only supported values are: "
                      "address, hw-address, duid");
        }
    }

    /// @brief specifies parameter types (true = query by address, false =
    ///         query by identifier-type,identifier)
    Type query_type;

    /// @brief Lease type (NA,TA or PD) used for v6 leases
    Lease::Type lease_type;

    /// @brief IAID identifier used for v6 leases
    uint32_t iaid;

    /// @brief Default constructor.
    Parameters()
        :addr("::"), query_type(TYPE_ADDR), lease_type(Lease::TYPE_NA),
         iaid(0) {
    }
};

private:

    /// @brief Registers commands:
    ///
    /// Registers:
    /// - lease4-add
    /// - lease6-add
    /// - lease4-get
    /// - lease6-get
    /// - lease4-del
    /// - lease6-del
    /// - lease4-update
    /// - lease6-update
    /// - lease4-wipe
    /// - lease6-wipe

    /// @throw Unexpected if CommandMgr is not available (should not happen)
    void registerCommands();

    /// @brief Deregisters commands:
    ///
    /// Deregisters:
    /// - lease4-add
    /// - lease6-add
    /// - lease4-get
    /// - lease6-get
    /// - lease4-del
    /// - lease6-del
    /// - lease4-update
    /// - lease6-update
    /// - lease4-wipe
    /// - lease6-wipe
    ///
    /// @throw Unexpected if CommandMgr is not available (should not happen)
    void deregisterCommands();

    /// @brief lease4-add, lease6-add command handler
    ///
    /// This command attempts to add a lease.
    ///
    /// An example full command looks as follows. Note that the args
    /// parameter is expected to contain the "arguments" portion of it.
    /// This function covers both v4 and v6 leases.
    ///
    /// Example command for v4:
    /// {
    ///     "command": "lease4-add",
    ///     "parameters": {
    ///         "address": "192.0.2.1",
    ///         "hwaddr": "00:01:02:03:04:05",
    ///         "client-id": "this-is-a-client",
    ///         "valid-lft": 3600,
    ///         "expire": 12345678,
    ///         "subnet-id": 1,
    ///         "fqdn-fwd": true,
    ///         "fqdn-rev": true,
    ///         "hostname": "myhost.example.org",
    ///         "state": 0
    ///     }
    /// }
    /// Example command for v6:
    /// {
    ///     "command": "lease6-add",
    ///     "arguments": {
    ///         "subnet-id": 66,
    ///         "ip-address": "2001:db8:abcd::",
    ///         "type": "IA_PD",
    ///         "prefix-len": 48,
    ///         "duid": "01:02:03:04:05:06:07:08",
    ///         "iaid": 1234,
    ///         "preferred-lft": 500,
    ///         "valid-lft": 1000,
    ///         "expire": 12345678,
    ///         "fqdn-fwd": true,
    ///         "fqdn-rev": true,
    ///         "hostname": "urania.example.org""
    ///     }
    /// }

    ///
    /// @param command should be 'lease4-add' or 'lease6-add'
    /// @param args must contain host reservation definition.
    /// @return result of the operation
    static ConstElementPtr
    leaseAddHandler(const string& command, ConstElementPtr args);

    /// @brief lease4-get, lease6-get command handler
    ///
    /// This command attempts to retrieve a lease that match selected criteria.
    /// The following types of parameters are supported:
    /// - (subnet-id, address) for both v4 and v6
    /// - (subnet-id, identifier-type, identifier) for v4
    /// - (subnet-id, type, iana, identifier-type, identifier) for v6
    ///
    /// Example command for query by (subnet-id, address):
    /// {
    ///     "command": "lease4-get",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "ip-address": "192.0.2.202"
    ///     }
    /// }
    ///
    /// Example command for query by (subnet-id, identifier-type, identifier)
    /// {
    ///     "command": "lease4-get",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "identifier-type": "hw-address",
    ///         "identifier": "00:01:02:03:04:05"
    ///     }
    /// }
    ///
    /// Example command for query by (subnet-id, type, iana, identifier-type,
    ///                               identifier):
    /// {
    ///     "command": "lease6-get",
    ///     "arguments": {
    ///     "subnet-id": 66,
    ///     "iaid": 42,
    ///     "type": "IA_NA",
    ///     "identifier-type": "duid",
    ///     "identifier": "77:77:77:77:77:77:77:77"
    ///     }
    /// }
    /// @param command "lease4-get" or "lease6-get"
    /// @param args must contain host reservation definition.
    /// @return result of the operation (includes lease details, if found)
    static ConstElementPtr
    leaseGetHandler(const string& command, ConstElementPtr args);

    /// @brief lease4-del command handler
    ///
    /// This command attempts to delete an IPv4 lease that match selected
    /// criteria. Two types of parameters are supported: (subnet-id, address) or
    /// (subnet-id, identifier-type, identifier).
    ///
    /// Example command for deletion by (subnet-id, address):
    /// {
    ///     "command": "lease4-del",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "ip-address": "192.0.2.202"
    ///     }
    /// }
    ///
    /// Example command for deletion by (subnet-id, identifier-type, identifier)
    /// {
    ///     "command": "lease4-del",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "identifier-type": "hw-address",
    ///         "identifier": "00:01:02:03:04:05"
    ///     }
    /// }";
    /// @param command should be 'lease4-del' (but it's ignored)
    /// @param args must contain host reservation definition.
    /// @return result of the operation (host will be included as parameters, if found)
    static ConstElementPtr
    lease4DelHandler(const string& command, ConstElementPtr args);

    /// @brief lease6-del command handler
    ///
    /// This command attempts to delete a lease that match selected criteria.
    /// Two types of parameters are supported: (subnet-id, address) or
    /// (subnet-id, type, iaid, identifier-type, identifier).
    ///
    /// Example command for deletion by (subnet-id, address):
    /// {
    ///     "command": "lease6-del",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "ip-address": "192.0.2.202"
    ///     }
    /// }
    ///
    /// Example command for deletion by (subnet-id, type, iaid, identifier-type,
    /// identifier):
    /// {
    ///     "command": "lease6-del",
    ///     "arguments": {
    ///         "subnet-id": 1,
    ///         "type": "IA_NA",
    ///         "iaid": 123456,
    ///         "identifier-type": "hw-address",
    ///         "identifier": "00:01:02:03:04:05"
    ///     }
    /// }";
    /// @param command should be 'lease6-del' (but it's ignored)
    /// @param args must contain host reservation definition.
    /// @return result of the operation (host will be included as parameters, if found)
    static ConstElementPtr
    lease6DelHandler(const string& command, ConstElementPtr args);

    /// @brief lease4-update handler
    ///
    /// This command attempts to update existing IPv4 lease. The parameters
    /// specified will replace existing lease. The only condition is that
    /// the IP address must not change. If you want to change the IP address,
    /// please use lease4-del and lease4-add instead.
    ///
    /// Example command:
    /// {
    ///     "command": "lease4-update",
    ///     "arguments": {
    ///         "subnet-id": 44,
    ///         "ip-address": "192.0.2.1",
    ///         "hw-address": "1a:1b:1c:1d:1e:1f",
    ///         "hostname": "newhostname.example.org"
    ///     }
    /// };
    ///
    /// @param command - should be "lease4-update", but it is ignored
    /// @param args arguments that describe the lease being updated.
    static ConstElementPtr
    lease4UpdateHandler(const string& command, ConstElementPtr args);

    /// @brief lease6-update handler
    ///
    /// This command attempts to update existing IPv6 lease. The parameters
    /// specified will replace existing lease. The only condition is that
    /// the IP address must not change. If you want to change the IP address,
    /// please use lease6-del and lease6-add instead.
    ///
    /// Example command:
    /// {
    ///     "command": "lease6-update",
    ///     "arguments": {
    ///         "subnet-id": 66,
    ///         "ip-address": "2001:db8::1",
    ///         "iaid": 7654321,
    ///         "duid": "88:88:88:88:88:88:88:88",
    ///         "hostname": "newhostname.example.org"
    ///     }
    /// }";
    ///
    /// @param command - should be "lease6-update" (but it is ignored)
    /// @param args arguments that describe the lease being updated.
    static ConstElementPtr
    lease6UpdateHandler(const string& command, ConstElementPtr args);

    /// @brief lease4-wipe handler
    ///
    /// This commands attempts to remove all IPv4 leases from a specific
    /// subnet. Currently the leases are removed from the database,
    /// without any processing (like calling hooks or doing DDNS
    /// cleanups).
    ///
    /// Example command:
    /// {\n"
    ///     "command": "lease4-wipe",\n"
    ///     "arguments": {"
    ///         "subnet-id": 44
    ///     }\n"
    /// }";
    /// @param command - should be "lease4-wipe" (but is ignored)
    /// @param args arguments that describe the lease being updated.
    static ConstElementPtr
    lease4WipeHandler(const string& command, ConstElementPtr args);

    /// @brief lease6-wipe handler
    ///
    /// This commands attempts to remove all IPv4 leases from a specific
    /// subnet. Currently the leases are removed from the database,
    /// without any processing (like calling hooks or doing DDNS
    /// cleanups).
    ///
    /// Example command:
    /// {\n"
    ///     "command": "lease4-wipe",\n"
    ///     "arguments": {"
    ///         "subnet-id": 44
    ///     }\n"
    /// }";
    /// @param command - should be "lease4-wipe" (but is ignored)
    /// @param args arguments that describe the lease being updated.
    static ConstElementPtr
    lease6WipeHandler(const string& command, ConstElementPtr args);

    /// @brief Extracts parameters required for reservation-get and reservation-del
    ///
    /// See @ref Parameters class for detailed description of what is expected
    /// in the args structure.
    ///
    /// @param v6 whether addresses allowed are v4 (false) or v6 (true)
    /// @param args arguments passed to command
    /// @return parsed parameters
    /// @throw BadValue if input arguments don't make sense.
    static Parameters getParameters(bool v6, const ConstElementPtr& args);
};

LeaseCmdsImpl::LeaseCmdsImpl() {
    registerCommands();
}

LeaseCmdsImpl::~LeaseCmdsImpl() {
    deregisterCommands();
}

void LeaseCmdsImpl::registerCommands() {
    /// @todo: Use registration mechanism once #5314 is merged.
    /// See #5321 discussion.
    CommandMgr::instance().registerCommand("lease4-add",
        boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2));
    CommandMgr::instance().registerCommand("lease6-add",
        boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2));

    CommandMgr::instance().registerCommand("lease4-get",
        boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2));
    CommandMgr::instance().registerCommand("lease6-get",
        boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2));

    CommandMgr::instance().registerCommand("lease4-del",
    boost::bind(&LeaseCmdsImpl::lease4DelHandler, _1, _2));
    CommandMgr::instance().registerCommand("lease6-del",
    boost::bind(&LeaseCmdsImpl::lease6DelHandler, _1, _2));

    CommandMgr::instance().registerCommand("lease4-update",
    boost::bind(&LeaseCmdsImpl::lease4UpdateHandler, _1, _2));
    CommandMgr::instance().registerCommand("lease6-update",
    boost::bind(&LeaseCmdsImpl::lease6UpdateHandler, _1, _2));

    CommandMgr::instance().registerCommand("lease4-wipe",
    boost::bind(&LeaseCmdsImpl::lease4WipeHandler, _1, _2));
    CommandMgr::instance().registerCommand("lease6-wipe",
    boost::bind(&LeaseCmdsImpl::lease6WipeHandler, _1, _2));
}

void LeaseCmdsImpl::deregisterCommands() {
    /// @todo: Use deregistration mechanism once #5321 discussion is done
    CommandMgr::instance().deregisterCommand("lease4-add");
    CommandMgr::instance().deregisterCommand("lease6-add");

    CommandMgr::instance().deregisterCommand("lease4-get");
    CommandMgr::instance().deregisterCommand("lease6-get");

    CommandMgr::instance().deregisterCommand("lease4-del");
    CommandMgr::instance().deregisterCommand("lease6-del");

    CommandMgr::instance().deregisterCommand("lease4-update");
    CommandMgr::instance().deregisterCommand("lease6-update");

    CommandMgr::instance().deregisterCommand("lease4-wipe");
    CommandMgr::instance().deregisterCommand("lease6-wipe");
}

ConstElementPtr
LeaseCmdsImpl::leaseAddHandler(const std::string& name,
                               ConstElementPtr params) {
    bool v4 = (name == "lease4-add");

    string txt = "(missing parameters)";
    if (params) {
        txt = params->str();
    }

    try {
        if (!params) {
            isc_throw(isc::BadValue, "no parameters specified for the command");
        }

        ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();

        Lease4Ptr lease4;
        Lease6Ptr lease6;
        if (v4) {
            Lease4Parser parser;
            lease4 = parser.parse(config, params);

            // checkLeaseIntegrity(config, lease4);

            if (lease4) {
                LeaseMgrFactory::instance().addLease(lease4);
            }

        } else {
            Lease6Parser parser;
            lease6 = parser.parse(config, params);

            // checkLeaseIntegrity(config, lease6);

            if (lease6) {
                LeaseMgrFactory::instance().addLease(lease6);
            }
        }

    } catch (const std::exception& ex) {
        LOG_ERROR(lease_cmds_logger, v4 ? LEASE_CMDS_ADD4_FAILED : LEASE_CMDS_ADD6_FAILED)
            .arg(txt)
            .arg(ex.what());
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
    LOG_INFO(lease_cmds_logger,
             v4 ? LEASE_CMDS_ADD4 : LEASE_CMDS_ADD6).arg(txt);
    return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease added."));
}

LeaseCmdsImpl::Parameters
LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) {
    Parameters x;

    if (!params || params->getType() != Element::map) {
        isc_throw(BadValue, "Parameters missing or are not a map.");
    }

    // We support several sets of parameters for leaseX-get/lease-del:
    // lease-get(type, address)
    // lease-get(type, subnet-id, identifier-type, identifier)

    if (params->contains("type")) {
        string t = params->get("type")->stringValue();
        if (t == "IA_NA" || t == "0") {
            x.lease_type = Lease::TYPE_NA;
        } else if (t == "IA_TA" || t == "1") {
            x.lease_type = Lease::TYPE_TA;
        } else if (t == "IA_PD" || t == "2") {
            x.lease_type = Lease::TYPE_PD;
        } else if (t == "V4" || t == "3") {
            x.lease_type = Lease::TYPE_V4;
        } else {
            isc_throw(BadValue, "Invalid lease type specified: "
                      << t << ", only supported values are: IA_NA, IA_TA,"
                      << " IA_PD and V4");
        }
    }

    ConstElementPtr tmp = params->get("ip-address");
    if (tmp) {
        if (tmp->getType() != Element::string) {
            isc_throw(BadValue, "'ip-address' is not a string.");
        }

        x.addr = IOAddress(tmp->stringValue());

        if ((v6 && !x.addr.isV6()) || (!v6 && !x.addr.isV4())) {
            stringstream txt;
            txt << "Invalid " << (v6 ? "IPv6" : "IPv4")
                << " address specified: " << tmp->stringValue();
            isc_throw(BadValue, txt.str());
        }

        x.query_type = Parameters::TYPE_ADDR;
        return (x);
    }

    tmp = params->get("subnet-id");
    if (!tmp) {
        isc_throw(BadValue, "Mandatory 'subnet-id' parameter missing.");
    }
    if (tmp->getType() != Element::integer) {
        isc_throw(BadValue, "'subnet-id' parameter is not integer.");
    }
    x.subnet_id = tmp->intValue();

    if (params->contains("iaid")) {
        x.iaid = params->get("iaid")->intValue();
    }

    // No address specified. Ok, so it must be identifier based query.
    // "identifier-type": "duid",
    // "identifier": "aa:bb:cc:dd:ee:..."

    ConstElementPtr type = params->get("identifier-type");
    ConstElementPtr ident = params->get("identifier");
    if (!type || type->getType() != Element::string) {
        isc_throw(BadValue, "No 'ip-address' provided"
                  " and 'identifier-type' is either missing or not a string.");
    }
    if (!ident || ident->getType() != Element::string) {
        isc_throw(BadValue, "No 'ip-address' provided"
                  " and 'identifier' is either missing or not a string.");
    }

    // Got the parameters. Let's see if their values make sense.
    // Try to convert identifier-type
    x.query_type = Parameters::txtToType(type->stringValue());

    switch (x.query_type) {
    case Parameters::TYPE_HWADDR: {
        HWAddr hw = HWAddr::fromText(ident->stringValue());
        x.hwaddr = HWAddrPtr(new HWAddr(hw));
        break;
    }
    case Parameters::TYPE_DUID: {
        DUID duid = DUID::fromText(ident->stringValue());
        x.duid = DuidPtr(new DUID(duid));
        break;
    }
    case Parameters::TYPE_ADDR: {
        // We should never get here. The address clause should have been caught
        // earlier.
        return (x);
    }
    default: {
        isc_throw(BadValue, "Identifier type " << type->stringValue() <<
                  " is not supported.");
    }
    }
    return (x);
}

ConstElementPtr
LeaseCmdsImpl::leaseGetHandler(const std::string& name, ConstElementPtr params) {
    Parameters p;
    Lease4Ptr lease4;
    Lease6Ptr lease6;
    bool v4 = (name == "lease4-get");
    try {
        p = getParameters(!v4, params);

        switch (p.query_type) {
        case Parameters::TYPE_ADDR: {
            // Query by address
            if (v4) {
                lease4 = LeaseMgrFactory::instance().getLease4(p.addr);
            } else {
                lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, p.addr);
            }
            break;
        }
        case Parameters::TYPE_HWADDR:
            if (v4) {
                if (!p.hwaddr) {
                    return (createAnswer(CONTROL_RESULT_ERROR,
                                         "Program error: Query by hw-address "
                                         "requires hwaddr to be specified"));
                }
                lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
            } else {
                return (createAnswer(CONTROL_RESULT_ERROR,
                                     "Query by hw-address is not allowed in v6."));
            }
            break;
        case Parameters::TYPE_DUID:
            if (!v4) {
                if (!p.duid) {
                    return (createAnswer(CONTROL_RESULT_ERROR,
                                         "Program error: Query by duid "
                                         "requires duid to be specified"));
                }
                lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
                                                               p.iaid, p.subnet_id);
            } else {
                return (createAnswer(CONTROL_RESULT_ERROR,
                                     "Query by duid is not allowed in v4."));
            }
            break;
        default: {
            stringstream tmp;
            tmp << "Unknown query type: " << static_cast<int>(p.query_type);
            return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
        }
        }
    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }

    ElementPtr lease_json;
    if (v4 && lease4) {
        lease_json = lease4->toElement();
        return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease found.", lease_json));
    }
    if (!v4 && lease6) {
        lease_json = lease6->toElement();
        return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease found.", lease_json));

    }

    // If we got here, the lease has not been found.
    return (createAnswer(CONTROL_RESULT_EMPTY, "Lease not found."));
}

ConstElementPtr
LeaseCmdsImpl::lease4DelHandler(const std::string& , ConstElementPtr params) {
    Parameters p;
    Lease4Ptr lease4;
    IOAddress addr(IOAddress::IPV4_ZERO_ADDRESS());
    try {
        p = getParameters(false, params);

        switch (p.query_type) {
        case Parameters::TYPE_ADDR: {

            // If address was specified explicitly, let's use it as is.
            addr = p.addr;
            break;
        }
        case Parameters::TYPE_HWADDR:
            if (!p.hwaddr) {
                return (createAnswer(CONTROL_RESULT_ERROR,
                                     "Program error: Query by hw-address "
                                     "requires hwaddr to be specified"));
            }

            // Let's see if there's such a lease at all.
            lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
            if (!lease4) {
                return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found."));
            }

            // Found it, can use it as is.
            addr = lease4->addr_;
            break;

        case Parameters::TYPE_DUID:
            return (createAnswer(CONTROL_RESULT_ERROR,
                                     "Delete by duid is not allowed in v4."));
            break;

        default: {
            stringstream tmp;
            tmp << "Unknown query type: " << static_cast<int>(p.query_type);
            return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
        }
        }

        if (LeaseMgrFactory::instance().deleteLease(addr)) {
            return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
        } else {
            return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found."));
        }
    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

ConstElementPtr
LeaseCmdsImpl::lease6DelHandler(const std::string& , ConstElementPtr params) {
    Parameters p;
    Lease6Ptr lease6;
    IOAddress addr(IOAddress::IPV6_ZERO_ADDRESS());
    try {
        p = getParameters(true, params);

        switch (p.query_type) {
        case Parameters::TYPE_ADDR: {

            // If address was specified explicitly, let's use it as is.
            addr = p.addr;
            break;
        }
        case Parameters::TYPE_HWADDR:
            return (createAnswer(CONTROL_RESULT_ERROR,
                                 "Delete by hw-address is not allowed in v6."));

        case Parameters::TYPE_DUID:
            if (!p.duid) {
                return (createAnswer(CONTROL_RESULT_ERROR,
                                     "Program error: Query by duid "
                                     "requires duid to be specified"));
            }

            // Let's see if there's such a lease at all.
            lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
                                                           p.iaid, p.subnet_id);
            if (!lease6) {
                return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found."));
            }

            // Found it, can use it as is.
            addr = lease6->addr_;
            break;

        default: {
            stringstream tmp;
            tmp << "Unknown query type: " << static_cast<int>(p.query_type);
            return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
        }
        }

        if (LeaseMgrFactory::instance().deleteLease(addr)) {
            return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
        } else {
            return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found."));
        }
    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

ConstElementPtr
LeaseCmdsImpl::lease4UpdateHandler(const string& , ConstElementPtr params) {
    try {

        // We need the lease to be specified.
        if (!params) {
            isc_throw(isc::BadValue, "no parameters specified for lease4-update command");
        }

        // Get the parameters specified by the user first.
        ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
        Lease4Ptr lease4;
        Lease4Parser parser;
        // The parser does sanity checks (if the address is in scope, if
        // subnet-id is valid, etc)
        lease4 = parser.parse(config, params);

        LeaseMgrFactory::instance().updateLease4(lease4);
        return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease updated."));

    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

ConstElementPtr
LeaseCmdsImpl::lease6UpdateHandler(const string& , ConstElementPtr params) {
    try {
        // We need the lease to be specified.
        if (!params) {
            isc_throw(isc::BadValue, "no parameters specified for lease6-update command");
        }

        // Get the parameters specified by the user first.
        ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
        Lease6Ptr lease6;
        Lease6Parser parser;
        // The parser does sanity checks (if the address is in scope, if
        // subnet-id is valid, etc)
        lease6 = parser.parse(config, params);

        LeaseMgrFactory::instance().updateLease6(lease6);
        return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease updated."));

    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

ConstElementPtr
LeaseCmdsImpl::lease4WipeHandler(const string& /*cmd*/, ConstElementPtr params) {
    try {

        // The subnet-id is a mandatory parameter.
        if (!params) {
            isc_throw(isc::BadValue, "no parameters specified for lease4-wipe command");
        }
        SimpleParser parser;
        SubnetID id = parser.getUint32(params, "subnet-id");

        size_t num = LeaseMgrFactory::instance().wipeLeases4(id);

        stringstream tmp;
        tmp << "Deleted " << num << " IPv4 lease(s).";
        return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY,
                             tmp.str()));
    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

ConstElementPtr
LeaseCmdsImpl::lease6WipeHandler(const string& /*cmd*/, ConstElementPtr params) {
    try {

        // The subnet-id is a mandatory parameter.
        if (!params) {
            isc_throw(isc::BadValue, "no parameters specified for lease6-wipe command");
        }
        SimpleParser parser;
        SubnetID id = parser.getUint32(params, "subnet-id");

        size_t num = LeaseMgrFactory::instance().wipeLeases6(id);

        stringstream tmp;
        tmp << "Deleted " << num << " IPv6 lease(s).";
        return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY,
                             tmp.str()));
    } catch (const std::exception& ex) {
        return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
    }
}

LeaseCmds::LeaseCmds()
    :impl_(new LeaseCmdsImpl()) {
}

LeaseCmds::~LeaseCmds() {
    impl_.reset();
}

};
};

ext: cc