// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE ExecutionPhaseGradSumDecTest

#include <../test_runner.hpp>
#include <boost/test/unit_test.hpp>
#include <string>
#include <popart/builder.hpp>
#include <popart/ir.hpp>
#include <popart/op/add.hpp>
#include <popart/op/identity.hpp>
#include <popart/op/init.hpp>
#include <popart/op/l1.hpp>
#include <popart/op/matmul.hpp>
#include <popart/op/reshape.hpp>
#include <popart/tensorinfo.hpp>
#include <popart/tensornames.hpp>

using namespace popart;

// Model: N parallel matmuls, sharing weights, separate execution phases
// _________________________________________
// phase 0:
// in0 -
//       \
//        Matmul0 -- ReLU ----.
//       /                    |
//  w0 -                      |
// ___________________________|_____________
// phase 2:                   |
// in1 -                      |
//       \                    |
//        Matmul1 -- ReLU --. |
//       /                  | |
//  w0 -                    | |
// _________________________|_|_____________
// phase 4:                 | |
// in2 -                    | |
//       \                  | |
//        Matmul2 -- ReLU - Sum -- out
//       /
//  w0 -
// _________________________________________

// Note that the number of ops from the loss to the original grad-sum is the
// same for all matmuls.
// We then verify that the grad of w0 is correctly added in the bwd phases:
// 1. Add to w0-grad in phase 4 (Matmul2)
// 2. Add to w0-grad in phase 6 (Matmul1)
// 3. Add to w0-grad in phase 8 (Matmul0)

BOOST_AUTO_TEST_CASE(TestDecomposeAcrossExecutionPhases) {
  TestRunner runner;
  runner.isTraining = true;
  int numLayers     = 3;
  int size          = 100;

  // Weights are [size, size], Input and Acts are [1, size]
  TensorInfo wInfo{"FLOAT", std::vector<int64_t>{size, size}};
  std::vector<TestTensor> inputs;
  std::vector<TestTensor> outputs;
  std::vector<float> w0Data(wInfo.nelms(), 0);
  ConstVoidData w0CVData{w0Data.data(), wInfo};

  runner.buildModel([&](auto &builder) {
    auto aiOnnx = builder.aiOnnxOpset9();
    TensorInfo inInfo{"FLOAT", std::vector<int64_t>{1, size}};
    auto w0 = builder.addInitializedInputTensor(w0CVData);
    std::vector<std::string> outVec;
    for (int layer = 0; layer < numLayers; layer++) {
      auto input = builder.addInputTensor(inInfo);
      auto out = aiOnnx.matmul({input, w0}, "mm_layer" + std::to_string(layer));
      builder.executionPhase(out, layer * 2);
      out = aiOnnx.relu({out}, "relu_layer" + std::to_string(layer));
      builder.executionPhase(out, layer * 2);
      outVec.push_back(out);
    }
    auto sum = aiOnnx.sum(outVec);
    auto l1  = builder.aiGraphcoreOpset1().l1loss({sum}, 0.1);
    builder.executionPhase(l1, (numLayers - 1) * 2);

    // To make introspecting the IR easy
    runner.opts.enableOutlining = false;
    // Enable feature-under-test
    runner.opts.decomposeGradSum = true;
    // Use every second execution phase only (maps to one IPU)
    runner.opts.executionPhaseSettings.phases = numLayers * 2 - 1;
    runner.opts.virtualGraphMode = VirtualGraphMode::ExecutionPhases;
    runner.patterns              = Patterns(PatternsLevel::Default);
    runner.loss                  = l1;

    return sum;
  });

  runner.checkIr([&](Ir &ir) {
    std::vector<Op *> schedule =
        ir.getOpSchedule({}, RequireOptimalSchedule::Yes);
    std::vector<Op *> gradPartialAddsOrder;
    size_t addOpNumber = 0;
    for (size_t i = 0; i < schedule.size(); i++) {
      Op *op = schedule.at(i);
      if (op->isConvertibleTo<AddLhsInplaceOp>() ||
          op->isConvertibleTo<AddRhsInplaceOp>()) {

        gradPartialAddsOrder.push_back(op);
        auto addLhs = AddOp::getArg0InIndex();
        auto addRhs = AddOp::getArg1InIndex();

        size_t mmLayer         = numLayers - 1 - addOpNumber;
        std::string mmLayerStr = "mm_layer" + std::to_string(mmLayer);

        ExecutionPhase expectedPhase = numLayers + 1 + addOpNumber * 2;
        // Check that the add occurs in the expected phase (4, 6, 8)
        BOOST_CHECK(op->getExecutionPhase() == expectedPhase);

        // Check that the rhs input is produced in the same phase
        BOOST_CHECK(
            op->input->tensor(addRhs)->getProducer()->getExecutionPhase() ==
            expectedPhase);

        // Check that the add comes as soon as possible
        // after the weight partial tensor becomes live
        auto checkForMatMulRhsReshape = [&](Op *op) {
          BOOST_CHECK(op->getName().find("MatMulOp_RhsReshape") !=
                      std::string::npos);
          BOOST_CHECK(op->getName().find(mmLayerStr) != std::string::npos);
          BOOST_CHECK(op->isConvertibleTo<ReshapeInplaceOp>());
        };

        auto checkForMatMulRhsGrad = [&](Op *op) {
          BOOST_CHECK(op->getName().find("MatMulRhsGradOp") !=
                      std::string::npos);
          BOOST_CHECK(op->getName().find(mmLayerStr) != std::string::npos);
          BOOST_CHECK(op->isConvertibleTo<MatMulOp>());
        };

        if (addOpNumber == 0) {
          BOOST_CHECK(schedule.at(i - 1)->isConvertibleTo<InitOp>());
          checkForMatMulRhsReshape(schedule.at(i - 2));
          checkForMatMulRhsGrad(schedule.at(i - 3));
        } else {
          checkForMatMulRhsReshape(schedule.at(i - 1));
          checkForMatMulRhsGrad(schedule.at(i - 2));
        }
        addOpNumber++;
      }
    }

    BOOST_CHECK_EQUAL(gradPartialAddsOrder.size(), numLayers);
  });
}


/*
 * Software License Agreement (Apache License)
 *
 * Copyright (c) 2014, Dan Solomon
 *
 * 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.
 */
/*
 * cart_trajectory_pt.cpp
 *
 *  Created on: Oct 3, 2014
 *      Author: Dan Solomon
 */

#include <tuple>
#include <map>
#include <algorithm>
#include <console_bridge/console.h>
#include <ros/console.h>
#include <boost/uuid/uuid_io.hpp>
#include "descartes_trajectory/cart_trajectory_pt.h"
#include <descartes_core/utils.h>

#define NOT_IMPLEMENTED_ERR(ret) logError("%s not implemented", __PRETTY_FUNCTION__); return ret;

const double EQUALITY_TOLERANCE = 0.0001f;

using namespace descartes_core;

namespace descartes_trajectory
{
 EigenSTL::vector_Affine3d uniform(const TolerancedFrame & frame, const double orient_increment,
                                  const double pos_increment)
{

   EigenSTL::vector_Affine3d rtn;

   if(pos_increment < 0.0 || orient_increment < 0.0)
   {
     ROS_WARN_STREAM("Negative position/orientation intcrement: " << pos_increment
                     << "/" << orient_increment);
     rtn.clear();
     return rtn;
   }

  Eigen::Affine3d sampled_frame;

  // Calculating the number of samples for each tolerance (position and orientation)
  size_t ntx, nty, ntz, nrx, nry, nrz;

  if(orient_increment > 0)
  {
    nrx = ((frame.orientation_tolerance.x_upper - frame.orientation_tolerance.x_lower)/orient_increment) + 1;
    nry = ((frame.orientation_tolerance.y_upper - frame.orientation_tolerance.y_lower)/orient_increment) + 1;
    nrz = ((frame.orientation_tolerance.z_upper - frame.orientation_tolerance.z_lower)/orient_increment) + 1;
  }
  else
  {
    nrx = nry = nrz = 1;
  }

  if(pos_increment > 0)
  {
    ntx = ((frame.position_tolerance.x_upper - frame.position_tolerance.x_lower)/pos_increment) + 1;
    nty = ((frame.position_tolerance.y_upper - frame.position_tolerance.y_lower)/pos_increment) + 1;
    ntz = ((frame.position_tolerance.z_upper - frame.position_tolerance.z_lower)/pos_increment) + 1;
  }
  else
  {
    ntx = nty = ntz = 1;
  }

  // Estimating the number of samples base on tolerance zones and sampling increment.
  size_t est_num_samples = ntx * nty * ntz * nrx * nry * nrz;

  ROS_DEBUG_STREAM("Estimated number of samples: " << est_num_samples << ", reserving space");
  rtn.reserve(est_num_samples);

  //TODO: The following for loops do not ensure that the rull range is sample (lower to upper)
  //since there could be round off error in the incrementing of samples.  As a result, the
  //exact upper bound may not be sampled.  Since this isn't a final implementation, this will
  //be ignored.
  double rx, ry, rz, tx, ty, tz;

  for(size_t ii = 0; ii < nrx; ++ii)
  {
    rx = frame.orientation_tolerance.x_lower + orient_increment * ii;
    for(size_t jj = 0; jj < nry; ++jj)
    {
      ry = frame.orientation_tolerance.y_lower + orient_increment * jj;
      for(size_t kk = 0; kk < nrz; ++kk)
      {
        rz = frame.orientation_tolerance.z_lower + orient_increment * kk;
        for(size_t ll = 0; ll < ntx; ++ll)
        {
          tx = frame.position_tolerance.x_lower + pos_increment * ll;
          for(size_t mm = 0; mm < nty; ++mm)
          {
            ty = frame.position_tolerance.y_lower + pos_increment * mm;
            for(size_t nn = 0; nn < ntz; ++nn)
            {
              tz = frame.position_tolerance.z_lower + pos_increment * nn;

/*              sampled_frame = Eigen::Translation3d(tx,ty,tz) *
                  Eigen::AngleAxisd(rx, Eigen::Vector3d::UnitX()) *
                  Eigen::AngleAxisd(ry, Eigen::Vector3d::UnitY()) *
                  Eigen::AngleAxisd(rz, Eigen::Vector3d::UnitZ());*/
              sampled_frame = descartes_core::utils::toFrame(tx,ty, tz,
                                             rx, ry, rz,descartes_core::utils::EulerConventions::XYZ);
              rtn.push_back(sampled_frame);
            }
          }
        }
      }
    }
  }
  ROS_DEBUG_STREAM("Uniform sampling of frame, utilizing orientation increment: " << orient_increment
                   << ", and position increment: " << pos_increment
                   << " resulted in " << rtn.size() << " samples");
  return rtn;
}

double distance(const std::vector<double>& j1, const std::vector<double>& j2)
{
 double rt = 0;
 double d;
 if(j1.size() == j2.size())
 {
   for(int i = 0 ; i < j1.size(); i++)
   {
     d = j1[i] - j2[i];
     rt += d*d;
   }
 }
 else
 {
   ROS_WARN_STREAM("Unequal size vectors, returning negative distance");
   return -1;
 }

 return std::sqrt(rt);
}

CartTrajectoryPt::CartTrajectoryPt(const descartes_core::TimingConstraint& timing)
  : descartes_core::TrajectoryPt(timing),
  tool_base_(Eigen::Affine3d::Identity()),
  tool_pt_(Eigen::Affine3d::Identity()),
  wobj_base_(Eigen::Affine3d::Identity()),
  wobj_pt_(Eigen::Affine3d::Identity()),
  pos_increment_(0.0),
  orient_increment_(0.0)
{}

CartTrajectoryPt::CartTrajectoryPt(const Frame &wobj_base, const TolerancedFrame &wobj_pt, const Frame &tool,
                 const TolerancedFrame &tool_pt, double pos_increment, double orient_increment,
                 const descartes_core::TimingConstraint& timing)
  : descartes_core::TrajectoryPt(timing),
  tool_base_(tool),
  tool_pt_(tool_pt),
  wobj_base_(wobj_base),
  wobj_pt_(wobj_pt),
  pos_increment_(pos_increment),
  orient_increment_(orient_increment)
{}

CartTrajectoryPt::CartTrajectoryPt(const TolerancedFrame &wobj_pt, double pos_increment,
                                   double orient_increment, const descartes_core::TimingConstraint& timing)
  : descartes_core::TrajectoryPt(timing),
  tool_base_(Eigen::Affine3d::Identity()),
  tool_pt_(Eigen::Affine3d::Identity()),
  wobj_base_(Eigen::Affine3d::Identity()),
  wobj_pt_(wobj_pt),
  pos_increment_(pos_increment),
  orient_increment_(orient_increment)
{}

CartTrajectoryPt::CartTrajectoryPt(const Frame &wobj_pt, const descartes_core::TimingConstraint& timing)
: descartes_core::TrajectoryPt(timing),
tool_base_(Eigen::Affine3d::Identity()),
tool_pt_(Eigen::Affine3d::Identity()),
wobj_base_(Eigen::Affine3d::Identity()),
wobj_pt_(wobj_pt),
pos_increment_(0),
orient_increment_(0)
{}

bool CartTrajectoryPt::getClosestCartPose(const std::vector<double> &seed_state,
                                          const RobotModel &model, Eigen::Affine3d &pose) const
{
  NOT_IMPLEMENTED_ERR(false);
}

bool CartTrajectoryPt::getNominalCartPose(const std::vector<double> &seed_state,
                                          const RobotModel &model, Eigen::Affine3d &pose) const
{
  /* Simply return wobj_pt expressed in world */
  pose = wobj_base_.frame * wobj_pt_.frame;
  return true;  //TODO can this ever return false?
}

bool CartTrajectoryPt::computeCartesianPoses(EigenSTL::vector_Affine3d &poses) const
{
  EigenSTL::vector_Affine3d sampled_wobj_pts = uniform(wobj_pt_, orient_increment_,
                                                     pos_increment_);
  EigenSTL::vector_Affine3d sampled_tool_pts = uniform(tool_pt_, orient_increment_,
                                                       pos_increment_);

  poses.clear();
  poses.reserve(sampled_wobj_pts.size()*sampled_tool_pts.size());
  for(size_t wobj_pt = 0; wobj_pt < sampled_wobj_pts.size(); ++wobj_pt)
  {
    for(size_t tool_pt = 0; tool_pt < sampled_tool_pts.size(); ++tool_pt)
    {
      Eigen::Affine3d pose = wobj_base_.frame * sampled_wobj_pts[wobj_pt] *
          sampled_tool_pts[tool_pt].inverse() * tool_base_.frame_inv;

      poses.push_back(pose);
    }
  }

  return !poses.empty();
}

void CartTrajectoryPt::getCartesianPoses(const RobotModel &model, EigenSTL::vector_Affine3d &poses) const
{
  EigenSTL::vector_Affine3d all_poses;
  poses.clear();

  if(computeCartesianPoses(all_poses))
  {
    poses.reserve(all_poses.size());
    for(const auto& pose: all_poses)
    {
      if(model.isValid(pose))
      {
        poses.push_back(pose);
      }
    }
  }
  else
  {
    ROS_ERROR("Failed for find ANY cartesian poses");
  }

  if( poses.empty())
  {
    ROS_WARN("Failed for find VALID cartesian poses, returning");
  }
  else
  {
    ROS_DEBUG_STREAM("Get cartesian poses, sampled: " << all_poses.size()
                     << ", with " << poses.size() << " valid(returned) poses");
  }

}

bool CartTrajectoryPt::getClosestJointPose(const std::vector<double> &seed_state,
                                           const RobotModel &model,
                                           std::vector<double> &joint_pose) const
{
  Eigen::Affine3d nominal_pose,candidate_pose;

  if(!model.getFK(seed_state,candidate_pose))
  {
    ROS_ERROR_STREAM("FK failed for seed pose for closest joint pose");
    return false;
  }

  // getting pose values
  Eigen::Vector3d t = candidate_pose.translation();
  Eigen::Vector3d rpy = candidate_pose.rotation().eulerAngles(0,1,2);

  std::vector< std::tuple<double,double,double> > vals =
  {
   std::make_tuple(t(0),wobj_pt_.position_tolerance.x_lower,wobj_pt_.position_tolerance.x_upper),
   std::make_tuple(t(1),wobj_pt_.position_tolerance.y_lower,wobj_pt_.position_tolerance.y_upper),
   std::make_tuple(t(2),wobj_pt_.position_tolerance.z_lower,wobj_pt_.position_tolerance.z_upper),
   std::make_tuple(rpy(0),wobj_pt_.orientation_tolerance.x_lower,wobj_pt_.orientation_tolerance.x_upper),
   std::make_tuple(rpy(1),wobj_pt_.orientation_tolerance.y_lower,wobj_pt_.orientation_tolerance.y_upper),
   std::make_tuple(rpy(2),wobj_pt_.orientation_tolerance.z_lower,wobj_pt_.orientation_tolerance.z_upper)
  };

  std::vector<double> closest_pose_vals = {t(0),t(1),t(2),rpy(0),rpy(1),rpy(2)};
  bool solve_ik = false;
  for(int i = 0; i < vals.size();i++)
  {
    auto &lower = std::get<1>(vals[i]);
    auto &upper = std::get<2>(vals[i]);
    auto &v = std::get<0>(vals[i]);

    if( std::abs(upper -lower) > EQUALITY_TOLERANCE)
    {
      auto bounds = std::make_pair(lower,upper);
      if(std::minmax({lower,v,upper}) != bounds)
      {
        solve_ik =  true;
        closest_pose_vals[i] = v < lower ? lower : upper;
        ROS_DEBUG("Cartesian nominal [%i] exceeded bounds: [val: %f, lower: %f, upper: %f]",i,v,lower,upper);
      }
    }
    else
    {
      if(std::abs(v-lower) > EQUALITY_TOLERANCE)
      {
        solve_ik =  true;
        ROS_DEBUG("Cartesian nominals [%i] differ: [val: %f, lower: %f, upper: %f]",i,v,lower,upper);
        closest_pose_vals[i] = lower;
      }
    }

  }

  if(solve_ik)
  {

    Eigen::Affine3d closest_pose = descartes_core::utils::toFrame(closest_pose_vals[0],
                                                                 closest_pose_vals[1],
                                                                 closest_pose_vals[2],
                                                                 closest_pose_vals[3],
                                                                 closest_pose_vals[4],
                                                                 closest_pose_vals[5],
                                                                 descartes_core::utils::EulerConventions::XYZ);
    if(!model.getIK(closest_pose,seed_state,joint_pose))
    {
      ROS_WARN_STREAM("Ik failed on closest pose");

      std::vector<std::vector<double> > joint_poses;
      getJointPoses(model,joint_poses);
      if(joint_poses.size() > 0)
      {

        ROS_WARN_STREAM("Closest cartesian pose not found, returning closest to seed joint pose");

        double sd = std::numeric_limits<double>::max();
        double d;
        for(auto j: joint_poses)
        {
          d = distance(seed_state,j);
          if(sd > d)
          {
            sd = d;
            joint_pose = j;
          }
        }

      }
      else
      {
        ROS_ERROR_STREAM("getClosestJointPose failed, no valid joint poses for this point");
        return false;
      }

      return true;
    }
  }
  else
  {

    joint_pose.assign(seed_state.begin(),seed_state.end());
  }

  return true;
}

bool CartTrajectoryPt::getNominalJointPose(const std::vector<double> &seed_state,
                                           const RobotModel &model,
                                           std::vector<double> &joint_pose) const
{
  Eigen::Affine3d robot_pose = wobj_base_.frame * wobj_pt_.frame *
      tool_pt_.frame_inv * tool_base_.frame_inv;
  return model.getIK(robot_pose, seed_state, joint_pose);
}

void CartTrajectoryPt::getJointPoses(const RobotModel &model,
                                     std::vector<std::vector<double> > &joint_poses) const
{
  joint_poses.clear();

  EigenSTL::vector_Affine3d poses;
  if(computeCartesianPoses(poses))
  {
    poses.reserve(poses.size());
    for(const auto& pose: poses)
    {
      std::vector<std::vector<double> > local_joint_poses;
      if(model.getAllIK(pose, local_joint_poses))
      {
        joint_poses.insert(joint_poses.end(), local_joint_poses.begin(), local_joint_poses.end());
      }
    }
  }
  else
  {
    ROS_ERROR("Failed for find ANY cartesian poses");
  }

  if( joint_poses.empty())
  {
    ROS_WARN("Failed for find ANY joint poses, returning");
  }
  else
  {
    ROS_DEBUG_STREAM("Get joint poses, sampled: " << poses.size()
                     << ", with " << joint_poses.size() << " valid(returned) poses");
  }
}

bool CartTrajectoryPt::isValid(const RobotModel &model) const
{
  Eigen::Affine3d robot_pose = wobj_base_.frame * wobj_pt_.frame *
      tool_pt_.frame_inv * tool_base_.frame_inv;
  return model.isValid(robot_pose);
}

bool CartTrajectoryPt::setDiscretization(const std::vector<double> &discretization)
{
  NOT_IMPLEMENTED_ERR(false);
}

} /* namespace descartes_trajectory */


#ifndef INCLUDED_STDDEFX
#include "stddefx.h"
#define INCLUDED_STDDEFX
#endif

#ifndef INCLUDED_GEO_MOORENEIGHBOURHOOD
#include "geo_mooreneighbourhood.h"
#define INCLUDED_GEO_MOORENEIGHBOURHOOD
#endif

// Library headers.
#ifndef INCLUDED_BOOST_MATH_SPECIAL_FUNCTIONS_ROUND
#include <boost/math/special_functions/round.hpp>
#define INCLUDED_BOOST_MATH_SPECIAL_FUNCTIONS_ROUND
#endif

// PCRaster library headers.

// Module headers.

/*!
  \file
  This file contains the implementation of the MooreNeighbourhood class.
*/

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

/*
namespace geo {

class MooreNeighbourhoodPrivate
{
public:

  MooreNeighbourhoodPrivate()
  {
  }

  ~MooreNeighbourhoodPrivate()
  {
  }

};

} // namespace geo
*/

//------------------------------------------------------------------------------
// DEFINITION OF STATIC MOORENEIGHBOURHOOD MEMBERS
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// DEFINITION OF MOORENEIGHBOURHOOD MEMBERS
//------------------------------------------------------------------------------

//! Constructor.
/*!
  \param     radius Radius of the neighbourhood.

  Concentric square rings of cells are added to the neighbourhood if the
  following holds for the cell to the west/north/east or south of the center
  cell:

  The center is at a distance <= toRadius of the center of the center cell.

  If you want to exclude the center cell use MooreNeighbourhood(size_t, size_t)
  with fromRadius 1.
*/
geo::MooreNeighbourhood::MooreNeighbourhood(double toRadius)

  : Neighbourhood(toRadius)

{
  init();
}

//! Constructor.
/*!
  \param     fromRadius Radius of inner border of donut.
  \param     toRadius Radius of outer border of donut.

  This constructor constructs a donut shaped neighbourhood in which the inner
  cells are not part of the neighbourhood. With this constructor it is possible
  to create square zones around a center cell.

  Concentric square rings of cells are added to the neighbourhood if the
  following holds for the cell to the west/north/east or south of the center
  cell:

  The center is at a distance >= \a fromRadius and <= toRadius of the center
  of the center cell.
*/
geo::MooreNeighbourhood::MooreNeighbourhood(double fromRadius, double toRadius)

  : Neighbourhood(fromRadius, toRadius)

{
  init();
}

//! Destructor.
/*!
*/
geo::MooreNeighbourhood::~MooreNeighbourhood()
{
}

//! Initialises the object.
/*!
*/
void geo::MooreNeighbourhood::init()
{
  for(size_t radius = static_cast<size_t>(boost::math::round(fromRadius()));
         radius <= static_cast<size_t>(boost::math::round(toRadius())); ++radius) {
    addRing(radius);
  }
}

//! Adds a ring with radius \a radius to the neighbourhood.
/*!
  \param     radius Radius of ring around center cell to add to neighbourhood.
*/
void geo::MooreNeighbourhood::addRing(size_t radius)
{
  PRECOND(radius <= this->radius());

  size_t upperLeft = this->radius() - radius;

  for(size_t row = upperLeft; row < upperLeft + 2 * radius + 1; ++row) {
    cell(row, upperLeft) = 1.0;
    cell(row, upperLeft + 2 * radius) = 1.0;
  }

  for(size_t col = upperLeft + 1; col < upperLeft + 2 * radius; ++col) {
    cell(upperLeft, col) = 1.0;
    cell(upperLeft + 2 * radius, col) = 1.0;
  }
}

//------------------------------------------------------------------------------
// DEFINITION OF FREE OPERATORS
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// DEFINITION OF FREE FUNCTIONS
//------------------------------------------------------------------------------


#include <boost/test/unit_test.hpp>

#include <Eigen/Geometry>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

#include <boost/serialization/shared_ptr.hpp>

#include <boost_serialization/EigenTypes.hpp>
#include <boost_serialization/BaseTypes.hpp>
#include <boost_serialization/BoostTypes.hpp>
#include <boost_serialization/DynamicSizeSerialization.hpp>

#include <boost/container/flat_set.hpp>
#include <boost/container/flat_map.hpp>

using namespace boost::serialization;

BOOST_AUTO_TEST_CASE(eigen_quaternion_serialization_test)
{
    Eigen::Quaterniond rot = Eigen::AngleAxisd(0.45 * M_PI, Eigen::Vector3d::UnitZ()) *
                            Eigen::AngleAxisd(0.5 * M_PI, Eigen::Vector3d::UnitY()) *
                            Eigen::AngleAxisd(0.27 * M_PI, Eigen::Vector3d::UnitX());

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << rot;

    // deserialize from string stream
    boost::archive::binary_iarchive ia(stream);
    Eigen::Quaterniond rot_2;
    ia >> rot_2;

    BOOST_CHECK(rot.matrix() == rot_2.matrix());
}

BOOST_AUTO_TEST_CASE(eigen_matrix_serialization_test)
{
    Eigen::Matrix3d m = 0.2 * Eigen::Matrix3d::Identity();

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << m;

    // deserialize from string stream
    boost::archive::binary_iarchive ia(stream);
    Eigen::MatrixXd m2;
    ia >> m2;

    BOOST_CHECK(m.rows() == m2.rows());
    BOOST_CHECK(m.cols() == m2.cols());
    BOOST_CHECK(m == m2);
}

BOOST_AUTO_TEST_CASE(eigen_transform_serialization_test)
{
    base::Transform3d transform_o(2.2 * Eigen::Matrix3d::Identity());

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << transform_o;

    // deserialize from string stream
    boost::archive::binary_iarchive ia(stream);
    base::Transform3d transform_i;
    ia >> transform_i;

    BOOST_CHECK_EQUAL(transform_o.matrix().isApprox(transform_i.matrix()), true);
}

BOOST_AUTO_TEST_CASE(boost_shared_ptr_serialization_test)
{
    boost::shared_ptr<Eigen::Quaterniond> rot_o(new Eigen::Quaterniond());

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << rot_o;

    // deserialize from string stream
    boost::archive::binary_iarchive *ia = new boost::archive::binary_iarchive(stream);
    boost::shared_ptr<Eigen::Quaterniond> rot_i;
    (*ia) >> rot_i;

    BOOST_CHECK_EQUAL(rot_o.use_count(), 1);
    // THIS IS BUG IN BOOST
    // the counter is 2 or more after the serialization
    // probably fixed up version 1.57
    BOOST_CHECK_EQUAL(rot_i.use_count(), 2);
    delete ia;
    BOOST_CHECK_EQUAL(rot_i.use_count(), 1);
}

BOOST_AUTO_TEST_CASE(boost_multi_array_serialization_test)
{
    boost::multi_array<double, 2> array_o;
    array_o.resize(boost::extents[13][7]);

    for (unsigned int x = 0; x < array_o.shape()[0]; ++x)
    {
        for (unsigned int y = 0; y < array_o.shape()[1]; ++y)
        {
            double cell_value = rand();
            array_o[x][y] = cell_value;
        }
    }

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << array_o;

    boost::archive::binary_iarchive ia(stream);
    boost::multi_array<double, 2> array_i;
    ia >> array_i;

    BOOST_CHECK_EQUAL(array_o.shape()[0], array_i.shape()[0]);
    BOOST_CHECK_EQUAL(array_o.shape()[1], array_i.shape()[1]);

    BOOST_CHECK(array_o == array_i);
}

BOOST_AUTO_TEST_CASE(dynamic_size_serialization_test)
{
    uint64_t size_u8 = 1;
    uint64_t size_u16 = ((uint64_t)std::numeric_limits<uint8_t>::max()) + 1;
    uint64_t size_u32 = ((uint64_t)std::numeric_limits<uint16_t>::max()) + 1;
    uint64_t size_u64 = ((uint64_t)std::numeric_limits<uint32_t>::max()) + 1;

    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    boost::archive::binary_iarchive ia(stream);

    uint64_t size = 0;
    stream.str(std::string());
    stream.clear();
    saveSizeValue(oa, size_u8);
    BOOST_CHECK_EQUAL(stream.str().size(), 1);
    loadSizeValue(ia, size);
    BOOST_CHECK_EQUAL(size, size_u8);

    size = 0;
    stream.str(std::string());
    stream.clear();
    saveSizeValue(oa, size_u16);
    BOOST_CHECK_EQUAL(stream.str().size(), 1+2);
    loadSizeValue(ia, size);
    BOOST_CHECK_EQUAL(size, size_u16);

    size = 0;
    stream.str(std::string());
    stream.clear();
    saveSizeValue(oa, size_u32);
    BOOST_CHECK_EQUAL(stream.str().size(), 1+2+4);
    loadSizeValue(ia, size);
    BOOST_CHECK_EQUAL(size, size_u32);

    size = 0;
    stream.str(std::string());
    stream.clear();
    saveSizeValue(oa, size_u64);
    BOOST_CHECK_EQUAL(stream.str().size(), 1+2+4+8);
    loadSizeValue(ia, size);
    BOOST_CHECK_EQUAL(size, size_u64);
}

template<class X>
void test_in_out(const X& x)
{
    std::stringstream stream;
    boost::archive::binary_oarchive oa(stream);
    oa << x;

    X y;
    boost::archive::binary_iarchive ia(stream);
    ia >> y;

    BOOST_CHECK(x == y);
}

template<class Set>
void test_set(int size) {
    Set set;
    typedef typename Set::value_type Value;

    for(int i=0; i<size; ++i)
        set.insert(Value(rand()));

    test_in_out(set);
}
template<class Map>
void test_map(int size) {
    typedef typename Map::key_type Key;
    typedef typename Map::mapped_type T;
    Map map;

    for(int i=0; i<size; ++i)
        map.insert(std::pair<Key, T>(rand(), rand()));

    test_in_out(map);
}

BOOST_AUTO_TEST_CASE(boost_flat_container_serialization_test)
{
    test_set<boost::container::flat_set<double> >(100);
    test_set<boost::container::flat_set<int> >(1000);
    test_set<boost::container::flat_multiset<int> >(100);
    test_set<boost::container::flat_multiset<double> >(1000);

    test_map<boost::container::flat_map<double, bool> >(100);
    test_map<boost::container::flat_map<int, double> >(1000);
    test_map<boost::container::flat_multimap<bool, float> >(100);
    test_map<boost::container::flat_multimap<double, int> >(1000);
}


#ifndef VIENNACL_LINALG_QR_METHOD_COMMON_HPP
#define VIENNACL_LINALG_QR_METHOD_COMMON_HPP

/* =========================================================================
   Copyright (c) 2010-2016, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

#include <cmath>

#ifdef VIENNACL_WITH_OPENCL
#include "viennacl/ocl/device.hpp"
#include "viennacl/ocl/handle.hpp"
#include "viennacl/ocl/kernel.hpp"
#include "viennacl/linalg/opencl/kernels/svd.hpp"
#endif

#ifdef VIENNACL_WITH_CUDA
#include "viennacl/linalg/cuda/matrix_operations.hpp"
#endif
#include "viennacl/meta/result_of.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/matrix.hpp"

//#include <boost/numeric/ublas/vector.hpp>
//#include <boost/numeric/ublas/io.hpp>

/** @file viennacl/linalg/qr-method-common.hpp
    @brief Common routines used for the QR method and SVD. Experimental.
*/

namespace viennacl
{
namespace linalg
{

const std::string SVD_HOUSEHOLDER_UPDATE_QR_KERNEL = "house_update_QR";
const std::string SVD_MATRIX_TRANSPOSE_KERNEL = "transpose_inplace";
const std::string SVD_INVERSE_SIGNS_KERNEL = "inverse_signs";
const std::string SVD_GIVENS_PREV_KERNEL = "givens_prev";
const std::string SVD_FINAL_ITER_UPDATE_KERNEL = "final_iter_update";
const std::string SVD_UPDATE_QR_COLUMN_KERNEL = "update_qr_column";
const std::string SVD_HOUSEHOLDER_UPDATE_A_LEFT_KERNEL = "house_update_A_left";
const std::string SVD_HOUSEHOLDER_UPDATE_A_RIGHT_KERNEL = "house_update_A_right";
const std::string SVD_HOUSEHOLDER_UPDATE_QL_KERNEL = "house_update_QL";

namespace detail
{
static const double EPS = 1e-10;
static const vcl_size_t ITER_MAX = 50;

template <typename SCALARTYPE>
SCALARTYPE pythag(SCALARTYPE a, SCALARTYPE b)
{
  return std::sqrt(a*a + b*b);
}

template <typename SCALARTYPE>
SCALARTYPE sign(SCALARTYPE val)
{
    return (val >= 0) ? SCALARTYPE(1) : SCALARTYPE(-1);
}

// DEPRECATED: Replace with viennacl::linalg::norm_2
template <typename VectorType>
typename VectorType::value_type norm_lcl(VectorType const & x, vcl_size_t size)
{
  typename VectorType::value_type x_norm = 0.0;
  for(vcl_size_t i = 0; i < size; i++)
    x_norm += static_cast<typename VectorType::value_type>(std::pow(x[i], 2));
  return static_cast<typename VectorType::value_type>(std::sqrt(x_norm));
}

template <typename VectorType>
void normalize(VectorType & x, vcl_size_t size)
{
  typename VectorType::value_type x_norm = norm_lcl(x, size);
  for(vcl_size_t i = 0; i < size; i++)
      x[i] /= x_norm;
}

template <typename VectorType>
void householder_vector(VectorType & v, vcl_size_t start)
{
  typedef typename VectorType::value_type    ScalarType;
  ScalarType x_norm = norm_lcl(v, v.size());
  ScalarType alpha = -sign(v[start]) * x_norm;
  v[start] += alpha;
  normalize(v, v.size());
}

template <typename SCALARTYPE>
void transpose(matrix_base<SCALARTYPE> & A)
{
  (void)A;
#ifdef VIENNACL_WITH_OPENCL

  viennacl::ocl::context & ctx = const_cast<viennacl::ocl::context &>(viennacl::traits::opencl_handle(A).context());
  if(A.row_major())
  {
      viennacl::linalg::opencl::kernels::svd<SCALARTYPE, row_major>::init(ctx);
      viennacl::ocl::kernel & kernel = viennacl::ocl::get_kernel(viennacl::linalg::opencl::kernels::svd<SCALARTYPE, row_major>::program_name(), SVD_MATRIX_TRANSPOSE_KERNEL);

      viennacl::ocl::enqueue(kernel(A,
                                    static_cast<cl_uint>(A.internal_size1()),
                                    static_cast<cl_uint>(A.internal_size2())
                                   )
                            );
  }
  else
  {
      viennacl::linalg::opencl::kernels::svd<SCALARTYPE, row_major>::init(ctx);
      viennacl::ocl::kernel & kernel = viennacl::ocl::get_kernel(viennacl::linalg::opencl::kernels::svd<SCALARTYPE, column_major>::program_name(), SVD_MATRIX_TRANSPOSE_KERNEL);

      viennacl::ocl::enqueue(kernel(A,
                                    static_cast<cl_uint>(A.internal_size1()),
                                    static_cast<cl_uint>(A.internal_size2())
                                   )
                            );
  }

#endif
}

template <typename T>
void cdiv(T xr, T xi, T yr, T yi, T& cdivr, T& cdivi)
{
    // Complex scalar division.
    T r;
    T d;
    if (std::fabs(yr) > std::fabs(yi))
    {
        r = yi / yr;
        d = yr + r * yi;
        cdivr = (xr + r * xi) / d;
        cdivi = (xi - r * xr) / d;
    }
    else
    {
        r = yr / yi;
        d = yi + r * yr;
        cdivr = (r * xr + xi) / d;
        cdivi = (r * xi - xr) / d;
    }
}

template<typename SCALARTYPE>
void prepare_householder_vector(
                              matrix_base<SCALARTYPE>& A,
                              vector_base<SCALARTYPE>& D,
                              vcl_size_t size,
                              vcl_size_t row_start,
                              vcl_size_t col_start,
                              vcl_size_t start,
                              bool is_column
                              )
{
  //boost::numeric::ublas::vector<SCALARTYPE> tmp = boost::numeric::ublas::scalar_vector<SCALARTYPE>(size, 0);
  std::vector<SCALARTYPE> tmp(size);
  copy_vec(A, D, row_start, col_start, is_column);
  fast_copy(D.begin(), D.begin() + vcl_ptrdiff_t(size - start), tmp.begin() + vcl_ptrdiff_t(start));

  detail::householder_vector(tmp, start);
  fast_copy(tmp, D);
}

} //detail
}
}

#endif


/*
 *  testVoxelize.cpp
 *  Trogdor6
 *
 *  Created by Paul C Hansen on 7/23/11.
 *  Copyright 2011 Stanford University. All rights reserved.
 *
 *  This file is covered by the MIT license.  See LICENSE.txt.
 */

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE testVoxelize

#include <boost/test/unit_test.hpp>

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Voxelize.h"

BOOST_AUTO_TEST_CASE( GetAndSet )
{
    Rect3d bounds(-1,-2,-3,1,2,3);
    Rect3i voxels(-1,-1,-1,1,1,1);

    Voxelize vox(bounds, voxels);
    BOOST_CHECK(vox.bounds() == bounds);
    BOOST_CHECK(vox.voxelBounds() == voxels);
    BOOST_CHECK(vox.voxelSize() == Vector3d(2.0/3.0, 4.0/3.0, 6.0/3.0));
    BOOST_CHECK(vox.voxelHalfWidth(0) == Vector2d(2.0/3.0, 1.0));
    BOOST_CHECK(vox.voxelHalfWidth(1) == Vector2d(1.0, 1.0/3.0));
    BOOST_CHECK(vox.voxelHalfWidth(2) == Vector2d(1.0/3.0, 2.0/3.0));
    BOOST_CHECK(vox.nonSymmetricDimensions() == Vector3b(true,true,true));

    BOOST_CHECK(vox.voxel(-1,-1,-1) == Rect3d(-1, -2, -3,
        -1+2.0/3, -2 + 4.0/3, -3 + 6.0/3));
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file portfolio/builders/fxforward.hpp
    \brief Engine builder for FX Forwards
    \ingroup builders
*/

#pragma once

#include <boost/make_shared.hpp>
#include <ored/portfolio/builders/cachingenginebuilder.hpp>
#include <ored/portfolio/enginefactory.hpp>
#include <qle/pricingengines/discountingfxforwardengine.hpp>

namespace ore {
namespace data {

//! Engine Builder base class for FX Forwards
/*! Pricing engines are cached by currency1 and currency2
    \ingroup builders
*/
class FxForwardEngineBuilderBase
    : public CachingPricingEngineBuilder<string, const Currency&, const Currency&> {
public:
    FxForwardEngineBuilderBase(const std::string& model, const std::string& engine)
        : CachingEngineBuilder(model, engine, {"FxForward"}) {}

protected:
    virtual string keyImpl(const Currency& forCcy, const Currency& domCcy) override {
        return forCcy.code() + domCcy.code();
    }
};

//! Engine Builder for FX Forwards
/*! Pricing engines are cached by currency pair
    \ingroup builders
*/
class FxForwardEngineBuilder : public FxForwardEngineBuilderBase {
public:
    FxForwardEngineBuilder() : FxForwardEngineBuilderBase("DiscountedCashflows", "DiscountingFxForwardEngine") {}

protected:
    virtual boost::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override {
        string pair = keyImpl(forCcy, domCcy);
        return boost::make_shared<QuantExt::DiscountingFxForwardEngine>(
            domCcy, market_->discountCurve(domCcy.code(), configuration(MarketContext::pricing)), forCcy,
            market_->discountCurve(forCcy.code(), configuration(MarketContext::pricing)),
            market_->fxSpot(pair, configuration(MarketContext::pricing)));
    }
};

} // namespace data
} // namespace ore


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#include "catch.hpp"

#include <iostream>

#include <Eigen/Core>

#include "bi_operators/CollocationIntegrator.hpp"
#include "green/DerivativeTypes.hpp"
#include "cavity/GePolCavity.hpp"
#include "green/Vacuum.hpp"
#include "green/UniformDielectric.hpp"
#include "solver/IEFSolver.hpp"

/*! \class IEFSolver
 *  \test \b NH3GePolRestart tests IEFSolver using ammonia with a GePol cavity read from .npz file
 */
TEST_CASE("Test solver for the IEFPCM for NH3 and a restarted GePol cavity", "[solver][iefpcm][iefpcm_gepol-NH3_from-file]")
{
    Eigen::Vector3d N( -0.000000000,   -0.104038047,    0.000000000);
    Eigen::Vector3d H1(-0.901584415,    0.481847022,   -1.561590016);
    Eigen::Vector3d H2(-0.901584415,    0.481847022,    1.561590016);
    Eigen::Vector3d H3( 1.803168833,    0.481847022,    0.000000000);
    // Load  cavity
    GePolCavity cavity;
    cavity.loadCavity("nh3.npz");

    double permittivity = 78.39;
    Vacuum<> gf_i;
    UniformDielectric<> gf_o(permittivity);
    bool symm = true;
    IEFSolver solver(symm);
    solver.buildSystemMatrix(cavity, gf_i, gf_o);

    double Ncharge = 7.0;
    double Hcharge = 1.0;
    int size = cavity.size();
    Eigen::VectorXd fake_mep = Eigen::VectorXd::Zero(size);
    for (int i = 0; i < size; ++i) {
        Eigen::Vector3d center = cavity.elementCenter(i);
        double Ndistance = (center - N).norm();
        double H1distance = (center - H1).norm();
        double H2distance = (center - H2).norm();
        double H3distance = (center - H3).norm();
        fake_mep(i) = Ncharge / Ndistance + Hcharge / H1distance + Hcharge / H2distance +
                      Hcharge / H3distance;
    }
    // The total ASC for a dielectric is -Q*(epsilon-1)/epsilon
    Eigen::VectorXd fake_asc = Eigen::VectorXd::Zero(size);
    fake_asc = solver.computeCharge(fake_mep);
    double totalASC = - (Ncharge + 3.0 * Hcharge) * (permittivity - 1) / permittivity;
    double totalFakeASC = fake_asc.sum();
    CAPTURE(totalASC - totalFakeASC);
    REQUIRE(totalASC == Approx(totalFakeASC).epsilon(1.0e-03));
}


// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2018-2021 The SorachanCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <db.h>
#include <txdb.h>
#include <init.h>
#include <miner.h>
#include <kernel.h>
#include <rpc/bitcoinrpc.h>
#include <block/block_process.h>
#include <miner/diff.h>
#include <block/block_alert.h>
#include <boost/format.hpp>
#include <util/strencodings.h>

json_spirit::Value CRPCTable::getsubsidy(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() > 1) {
        return data.JSONRPCSuccess(
            "getsubsidy [nTarget]\n"
            "Returns proof-of-work subsidy value for the specified value of target.");
    }

    unsigned int nBits = 0;
    if (params.size() != 0) {
        json_spirit::json_flags status;
        std::string str = params[0].get_str(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        CBigNum bnTarget(uint256(str.c_str()));
        nBits = bnTarget.GetCompact();
    } else
        nBits = diff::spacing::GetNextTargetRequired(block_info::pindexBest, false);

    return data.JSONRPCSuccess((uint64_t)diff::reward::GetProofOfWorkReward(nBits));
}

json_spirit::Value CRPCTable::getmininginfo(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() != 0) {
        return data.JSONRPCSuccess(
            "getmininginfo\n"
            "Returns an object containing mining-related information.");
    }

    json_spirit::Object obj, diff;
    obj.push_back(json_spirit::Pair("blocks", (int)block_info::nBestHeight));
    obj.push_back(json_spirit::Pair("currentblocksize", (uint64_t)block_info::nLastBlockSize));
    obj.push_back(json_spirit::Pair("currentblocktx", (uint64_t)block_info::nLastBlockTx));

    diff.push_back(json_spirit::Pair("proof-of-work", GetDifficulty()));
    diff.push_back(json_spirit::Pair("proof-of-stake", GetDifficulty(diff::spacing::GetLastBlockIndex(block_info::pindexBest, true))));
    diff.push_back(json_spirit::Pair("search-interval", (int)block_info::nLastCoinStakeSearchInterval));
    obj.push_back(json_spirit::Pair("difficulty", diff));

    obj.push_back(json_spirit::Pair("blockvalue", (uint64_t)diff::reward::GetProofOfWorkReward(diff::spacing::GetLastBlockIndex(block_info::pindexBest, false)->get_nBits())));
    obj.push_back(json_spirit::Pair("netmhashps", GetPoWMHashPS()));
    obj.push_back(json_spirit::Pair("netstakeweight", GetPoSKernelPS()));
    obj.push_back(json_spirit::Pair("errors", block_alert::GetWarnings("statusbar")));
    obj.push_back(json_spirit::Pair("pooledtx", (uint64_t)CTxMemPool::mempool.size()));

    obj.push_back(json_spirit::Pair("stakeinputs", (uint64_t)miner::nStakeInputsMapSize));
    obj.push_back(json_spirit::Pair("stakeinterest", diff::reward::GetProofOfStakeReward(0, diff::spacing::GetLastBlockIndex(block_info::pindexBest, true)->get_nBits(), diff::spacing::GetLastBlockIndex(block_info::pindexBest, true)->get_nTime(), true)));

    obj.push_back(json_spirit::Pair("testnet", (bool)args_bool::fTestNet));
    return data.JSONRPCSuccess(obj);
}

// scaninput '{"txid":"95d640426fe66de866a8cf2d0601d2c8cf3ec598109b4d4ffa7fd03dad6d35ce","difficulty":0.01, "days":10}'
json_spirit::Value CRPCTable::scaninput(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() != 1) {
        return data.JSONRPCSuccess(
            "scaninput '{\"txid\":\"txid\", \"vout\":[vout1, vout2, ..., voutN], \"difficulty\":difficulty, \"days\":days}'\n"
            "Scan specified transaction or input for suitable kernel solutions.\n"
            "    difficulty - upper limit for difficulty, current difficulty by default;\n"
            "    days - time window, 90 days by default.\n"
        );
    }

    bitrpc::RPCTypeCheck(data, params, boost::assign::list_of(json_spirit::obj_type));
    if(! data.fSuccess()) return data.JSONRPCError();

    json_spirit::json_flags status;
    json_spirit::Object scanParams = params[0].get_obj(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    const json_spirit::Value &txid_v = find_value(scanParams, "txid");
    if (txid_v.type() != json_spirit::str_type)
        return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");

    std::string txid = txid_v.get_str(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    if (! strenc::IsHex(txid))
        return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");

    uint256 hash(txid);
    int32_t nDays = 90;
    uint32_t nBits = diff::spacing::GetNextTargetRequired(block_info::pindexBest, true);
    const json_spirit::Value &diff_v = find_value(scanParams, "difficulty");
    if (diff_v.type() == json_spirit::real_type || diff_v.type() == json_spirit::int_type) {
        double dDiff = diff_v.get_real(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        if (dDiff <= 0)
            return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, diff must be greater than zero");

        CBigNum bnTarget(diff::nPoWBase);
        bnTarget *= 1000;
        bnTarget /= (int) (dDiff * 1000);
        nBits = bnTarget.GetCompact();
    }

    const json_spirit::Value &days_v = find_value(scanParams, "days");
    if (days_v.type() == json_spirit::int_type) {
        nDays = days_v.get_int(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        if (nDays <= 0)
            return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, interval length must be greater than zero");
    }

    CTransaction tx;
    uint256 hashBlock = 0;
    if (block_transaction::manage::GetTransaction(hash, tx, hashBlock)) {
        if (hashBlock == 0)
            return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to find transaction in the blockchain");

        std::vector<int> vInputs(0);
        const json_spirit::Value &inputs_v = find_value(scanParams, "vout");
        if (inputs_v.type() == json_spirit::array_type) {
            json_spirit::Array inputs = inputs_v.get_array(status);
            if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
            for(const json_spirit::Value &v_out: inputs) {
                int nOut = v_out.get_int(status);
                if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
                if (nOut < 0 || nOut > (int)tx.get_vout().size() - 1) {
                    std::stringstream strErrorMsg;
                    strErrorMsg << boost::format("Invalid parameter, input number %d is out of range") % nOut;
                    return data.JSONRPCError(RPC_INVALID_PARAMETER, strErrorMsg.str());
                }
                vInputs.push_back(nOut);
            }
        } else if(inputs_v.type() == json_spirit::int_type) {
            int nOut = inputs_v.get_int(status);
            if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
            if (nOut < 0 || nOut > (int)tx.get_vout().size() - 1) {
                std::stringstream strErrorMsg;
                strErrorMsg << boost::format("Invalid parameter, input number %d is out of range") % nOut;
                return data.JSONRPCError(RPC_INVALID_PARAMETER, strErrorMsg.str());
            }
            vInputs.push_back(nOut);
        } else {
            for (size_t i = 0; i != tx.get_vout().size(); ++i)
                vInputs.push_back(i);
        }

        CTxDB txdb("r");
        CBlock block;
        CTxIndex txindex;

        // Load transaction index item
        if (! txdb.ReadTxIndex(tx.GetHash(), txindex))
            return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to read block index item");

        // Read block header
        if (! block.ReadFromDisk(txindex.get_pos().get_nFile(), txindex.get_pos().get_nBlockPos(), false))
            return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "CBlock::ReadFromDisk() failed");

        uint64_t nStakeModifier = 0;
        if (! bitkernel<uint256>::GetKernelStakeModifier(block.GetHash(), nStakeModifier))
            return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No kernel stake modifier generated yet");

        std::pair<uint32_t, uint32_t> interval;
        interval.first = bitsystem::GetTime();

        // Only count coins meeting min age requirement
        if (block_check::nStakeMinAge + block.get_nTime() > interval.first)
            interval.first += (block_check::nStakeMinAge + block.get_nTime() - interval.first);
        interval.second = interval.first + nDays * util::nOneDay;

        json_spirit::Array results;
        for(const int &nOut: vInputs) {
            // Check for spent flag
            // It doesn't make sense to scan spent inputs.
            if (! txindex.get_vSpent(nOut).IsNull())
                continue;

            // Skip zero value outputs
            if (tx.get_vout(nOut).get_nValue() == 0)
                continue;

            // Build static part of kernel
            CDataStream ssKernel;
            ssKernel << nStakeModifier;
            ssKernel << block.get_nTime() << (txindex.get_pos().get_nTxPos() - txindex.get_pos().get_nBlockPos()) << tx.get_nTime() << nOut;
            CDataStream::const_iterator itK = ssKernel.begin();
            std::vector<std::pair<uint256, uint32_t> > result;
            if (bitkernel<uint256>::ScanKernelForward((unsigned char *)&itK[0], nBits, tx.get_nTime(), tx.get_vout(nOut).get_nValue(), interval, result)) {
                for(const std::pair<uint256, uint32_t> solution: result) {
                    json_spirit::Object item;
                    item.push_back(json_spirit::Pair("nout", nOut));
                    item.push_back(json_spirit::Pair("hash", solution.first.GetHex()));
                    item.push_back(json_spirit::Pair("time", util::DateTimeStrFormat(solution.second)));
                    results.push_back(item);
                }
            }
        }
        if (results.size() == 0)
            return data.JSONRPCSuccess(false);

        return data.JSONRPCSuccess(results);
    } else
        return data.JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
}

json_spirit::Value CRPCTable::getworkex(const json_spirit::Array &params, CBitrpcData &data) {
    using mapNewBlock_t = std::map<uint256, std::pair<CBlock *, CScript> >;
    static mapNewBlock_t mapNewBlock;
    static std::vector<CBlock *> vNewBlock;
    static CReserveKey reservekey(entry::pwalletMain);

    if (data.fHelp() || params.size() > 2) {
        return data.JSONRPCSuccess(
            "getworkex [data, coinbase]\n"
            "If [data, coinbase] is not specified, returns extended work data.\n"
        );
    }

    LOCK(CRPCTable::cs_getwork);
    if (net_node::vNodes.empty())
        return data.JSONRPCError(-9, strCoinName " is not connected!");
    if (block_notify<uint256>::IsInitialBlockDownload())
        return data.JSONRPCError(-10, strCoinName " is downloading blocks...");
    if (params.size() == 0) {
        // Update block
        static unsigned int nTransactionsUpdatedLast = 0;
        static CBlockIndex *pindexPrev = nullptr;
        static int64_t nStart = 0;
        static CBlock *pblock = nullptr;

        if (pindexPrev != block_info::pindexBest || (block_info::nTransactionsUpdated != nTransactionsUpdatedLast && bitsystem::GetTime() - nStart > 60)) {
            if (pindexPrev != block_info::pindexBest) {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                for(CBlock *pblock: vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }

            nTransactionsUpdatedLast = block_info::nTransactionsUpdated;
            pindexPrev = block_info::pindexBest;
            nStart = bitsystem::GetTime();

            // Create new block
            pblock = miner::CreateNewBlock(entry::pwalletMain);
            if (! pblock)
                return data.JSONRPCError(-7, "Out of memory");
            vNewBlock.push_back(pblock);
        }

        // Update nTime
        pblock->set_nTime(std::max(pindexPrev->GetMedianTimePast()+1, bitsystem::GetAdjustedTime()));
        pblock->set_nNonce(0);

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        miner::IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->get_hashMerkleRoot()] = std::make_pair(pblock, pblock->get_vtx(0).get_vin(0).get_scriptSig());

        // Prebuild hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        miner::FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->get_nBits()).getuint256();
        CTransaction coinbaseTx = pblock->get_vtx(0);
        auto merkle = pblock->GetMerkleBranch(0);
        json_spirit::Object result;
        result.push_back(json_spirit::Pair("data",     util::HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(json_spirit::Pair("target",   util::HexStr(BEGIN(hashTarget), END(hashTarget))));

        CDataStream ssTx;
        ssTx << coinbaseTx;
        result.push_back(json_spirit::Pair("coinbase", util::HexStr(ssTx.begin(), ssTx.end())));
        json_spirit::Array merkle_arr;
        for(uint256 merkleh: merkle)
            merkle_arr.push_back(util::HexStr(BEGIN(merkleh), END(merkleh)));

        result.push_back(json_spirit::Pair("merkle", merkle_arr));
        return data.JSONRPCSuccess(result);
    } else {
        // Parse parameters
        json_spirit::json_flags status;
        std::string str = params[0].get_str(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        rpctable_vector vchData = strenc::ParseHex(str);
        rpctable_vector coinbase;
        if(params.size() == 2) {
            str = params[1].get_str(status);
            if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
            coinbase = strenc::ParseHex(str);
        }
        if (vchData.size() != 128)
            return data.JSONRPCError(-8, "Invalid parameter");

        CBlockHeader<uint256> *pdata = (CBlockHeader<uint256> *)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128 / 4; ++i)
            ((unsigned int *)pdata)[i] = util::ByteReverse(((unsigned int *)pdata)[i]);

        // Get saved block
        if (! mapNewBlock.count(pdata->get_hashMerkleRoot()))
            return false;

        CBlock *pblock = mapNewBlock[pdata->get_hashMerkleRoot()].first;
        pblock->set_nTime(pdata->get_nTime());
        pblock->set_nNonce(pdata->get_nNonce());
        if(coinbase.size() == 0)
            pblock->set_vtx(0).set_vin(0).set_scriptSig(mapNewBlock[pdata->get_hashMerkleRoot()].second);
        else {
            // check vtx size
            CTransaction ctx;
            CDataStream(coinbase, SER_NETWORK, version::PROTOCOL_VERSION) >> ctx; // [OK] FIXME - HACK!
            pblock->set_vtx(0) = ctx;
        }

        pblock->set_hashMerkleRoot(pblock->BuildMerkleTree());
        return data.JSONRPCSuccess(miner::CheckWork(pblock, *entry::pwalletMain, reservekey));
    }
}

json_spirit::Value CRPCTable::getwork(const json_spirit::Array &params, CBitrpcData &data) {
    using mapNewBlock_t = std::map<uint256, std::pair<CBlock *, CScript> >;
    static mapNewBlock_t mapNewBlock;
    static std::vector<CBlock *> vNewBlock;
    static CReserveKey reservekey(entry::pwalletMain);

    if (data.fHelp() || params.size() > 1) {
        return data.JSONRPCSuccess(
            "getwork [data]\n"
            "If [data] is not specified, returns formatted hash data to work on:\n"
            "  \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
            "  \"data\" : block data\n"
            "  \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
            "  \"target\" : little endian hash target\n"
            "If [data] is specified, tries to solve the block and returns true if it was successful.");
    }

    LOCK(CRPCTable::cs_getwork);
    if (net_node::vNodes.empty())
        return data.JSONRPCError(RPC_CLIENT_NOT_CONNECTED, strCoinName " is not connected!");
    if (block_notify<uint256>::IsInitialBlockDownload())
        return data.JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, strCoinName " is downloading blocks...");
    if (params.size() == 0) {
        // Update block
        static unsigned int nTransactionsUpdatedLast = 0;
        static CBlockIndex *pindexPrev = nullptr;
        static int64_t nStart = 0;
        static CBlock *pblock = nullptr;

        if (pindexPrev != block_info::pindexBest ||
           (block_info::nTransactionsUpdated != nTransactionsUpdatedLast && bitsystem::GetTime() - nStart > 60)) {
            if (pindexPrev != block_info::pindexBest) {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                for(CBlock *pblock: vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }

            // Clear pindexPrev so future getworks make a new block, despite any failures from here on
            pindexPrev = nullptr;

            // Store the block_info::pindexBest used before miner::CreateNewBlock, to avoid races
            nTransactionsUpdatedLast = block_info::nTransactionsUpdated;
            CBlockIndex *pindexPrevNew = block_info::pindexBest;
            nStart = bitsystem::GetTime();

            // Create new block
            //logging::LogPrintf("ThreadRPCServer3 getwork new Block\n");
            pblock = miner::CreateNewBlock(entry::pwalletMain);
            if (! pblock)
                return bitjson::JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
            vNewBlock.push_back(pblock);

            // Need to update only after we know miner::CreateNewBlock succeeded
            pindexPrev = pindexPrevNew;
        }

        // Update nTime
        pblock->UpdateTime(pindexPrev);
        pblock->set_nNonce(0);

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        miner::IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->get_hashMerkleRoot()] = std::make_pair(pblock, pblock->get_vtx(0).get_vin(0).get_scriptSig());

        // Pre-build hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        miner::FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->get_nBits()).getuint256();
        json_spirit::Object result;
        result.push_back(json_spirit::Pair("midstate", util::HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
        result.push_back(json_spirit::Pair("data",     util::HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(json_spirit::Pair("hash1",    util::HexStr(BEGIN(phash1), END(phash1)))); // deprecated
        result.push_back(json_spirit::Pair("target",   util::HexStr(BEGIN(hashTarget), END(hashTarget))));
        return data.JSONRPCSuccess(result);
    } else {
        // Parse parameters
        json_spirit::json_flags status;
        std::string str = params[0].get_str(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        rpctable_vector vchData = strenc::ParseHex(str);
        if (vchData.size() != 128)
            return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");

        CBlockHeader<uint256> *pdata = (CBlockHeader<uint256> *)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128 / 4; ++i)
            ((unsigned int *)pdata)[i] = util::ByteReverse(((unsigned int *)pdata)[i]);

        // Get saved block
        if (! mapNewBlock.count(pdata->get_hashMerkleRoot()))
            return data.JSONRPCSuccess(false);

        CBlock *pblock = mapNewBlock[pdata->get_hashMerkleRoot()].first;
        pblock->set_nTime(pdata->get_nTime());
        pblock->set_nNonce(pdata->get_nNonce());
        pblock->set_vtx(0).set_vin(0).set_scriptSig(mapNewBlock[pdata->get_hashMerkleRoot()].second);
        pblock->set_hashMerkleRoot(pblock->BuildMerkleTree());
        return data.JSONRPCSuccess(miner::CheckWork(pblock, *entry::pwalletMain, reservekey));
    }
}

json_spirit::Value CRPCTable::getblocktemplate(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() > 1) {
        return data.JSONRPCSuccess(
            "getblocktemplate [params]\n"
            "Returns data needed to construct a block to work on:\n"
            "  \"version\" : block version\n"
            "  \"previousblockhash\" : hash of current highest block\n"
            "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
            "  \"coinbaseaux\" : data that should be included in coinbase\n"
            "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
            "  \"target\" : hash target\n"
            "  \"mintime\" : minimum timestamp appropriate for next block\n"
            "  \"curtime\" : current timestamp\n"
            "  \"mutable\" : list of ways the block template may be changed\n"
            "  \"noncerange\" : range of valid nonces\n"
            "  \"sigoplimit\" : limit of sigops in blocks\n"
            "  \"sizelimit\" : limit of block size\n"
            "  \"bits\" : compressed target of next block\n"
            "  \"height\" : height of the next block\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");
    }

    LOCK(CRPCTable::cs_getwork);
    std::string strMode = "template";
    if (params.size() > 0) { // json_spirit::null_type do nothing
        json_spirit::json_flags status;
        const json_spirit::Object &oparam = params[0].get_obj(status);
        if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        const json_spirit::Value &modeval = find_value(oparam, "mode");
        if (modeval.type() == json_spirit::str_type) {
            strMode = modeval.get_str(status);
            if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
        } else
            return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
    }

    if (strMode != "template")
        return data.JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
    if (net_node::vNodes.empty())
        return data.JSONRPCError(RPC_CLIENT_NOT_CONNECTED, strCoinName " is not connected!");
    if (block_notify<uint256>::IsInitialBlockDownload())
        return data.JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, strCoinName " is downloading blocks...");

    //static CReserveKey reservekey(entry::pwalletMain);

    // Update block
    static unsigned int nTransactionsUpdatedLast = 0;
    static CBlockIndex *pindexPrev = nullptr;
    static int64_t nStart = 0;
    static CBlock *pblock = nullptr;
    if (pindexPrev != block_info::pindexBest || (block_info::nTransactionsUpdated != nTransactionsUpdatedLast && bitsystem::GetTime() - nStart > 5)) {
        // Clear pindexPrev so future calls make a new block, despite any failures from here on
        pindexPrev = nullptr;

        // Store the block_info::pindexBest used before miner::CreateNewBlock, to avoid races
        nTransactionsUpdatedLast = block_info::nTransactionsUpdated;
        CBlockIndex *pindexPrevNew = block_info::pindexBest;
        nStart = bitsystem::GetTime();

        // Create new block
        if(pblock) {
            delete pblock;
            pblock = nullptr;
        }

        pblock = miner::CreateNewBlock(entry::pwalletMain);
        if (! pblock)
            return data.JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");

        // Need to update only after we know miner::CreateNewBlock succeeded
        pindexPrev = pindexPrevNew;
    }

    // Update nTime
    pblock->UpdateTime(pindexPrev);
    pblock->set_nNonce(0);

    json_spirit::Array transactions;
    std::map<uint256, int64_t> setTxIndex;
    int i = 0;
    CTxDB txdb("r");
    for(CTransaction &tx: pblock->set_vtx()) {
        uint256 txHash = tx.GetHash();
        setTxIndex[txHash] = i++;
        if (tx.IsCoinBase() || tx.IsCoinStake())
            continue;

        json_spirit::Object entry;
        CDataStream ssTx;
        ssTx << tx;
        entry.push_back(json_spirit::Pair("data", util::HexStr(ssTx.begin(), ssTx.end())));
        entry.push_back(json_spirit::Pair("hash", txHash.GetHex()));

        MapPrevTx mapInputs;
        std::map<uint256, CTxIndex> mapUnused;
        bool fInvalid = false;
        if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) {
            entry.push_back(json_spirit::Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));
            json_spirit::Array deps;
            for(MapPrevTx::value_type &inp: mapInputs) {
                if (setTxIndex.count(inp.first))
                    deps.push_back(setTxIndex[inp.first]);
            }
            entry.push_back(json_spirit::Pair("depends", deps));

            int64_t nSigOps = tx.GetLegacySigOpCount();
            nSigOps += tx.GetP2SHSigOpCount(mapInputs);
            entry.push_back(json_spirit::Pair("sigops", nSigOps));
        }

        transactions.push_back(entry);
    }

    json_spirit::Object aux;
    aux.push_back(json_spirit::Pair("flags", util::HexStr(block_info::COINBASE_FLAGS.begin(), block_info::COINBASE_FLAGS.end())));

    uint256 hashTarget = CBigNum().SetCompact(pblock->get_nBits()).getuint256();
    static json_spirit::Array aMutable;
    if (aMutable.empty()) {
        aMutable.push_back("time");
        aMutable.push_back("transactions");
        aMutable.push_back("prevblock");
    }

    json_spirit::Object result;
    result.push_back(json_spirit::Pair("version", pblock->get_nVersion()));
    result.push_back(json_spirit::Pair("previousblockhash", pblock->get_hashPrevBlock().GetHex()));
    result.push_back(json_spirit::Pair("transactions", transactions));
    result.push_back(json_spirit::Pair("coinbaseaux", aux));
    result.push_back(json_spirit::Pair("coinbasevalue", (int64_t)pblock->get_vtx(0).get_vout(0).get_nValue()));
    result.push_back(json_spirit::Pair("target", hashTarget.GetHex()));
    result.push_back(json_spirit::Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
    result.push_back(json_spirit::Pair("mutable", aMutable));
    result.push_back(json_spirit::Pair("noncerange", "00000000ffffffff"));
    result.push_back(json_spirit::Pair("sigoplimit", (int64_t)block_params::MAX_BLOCK_SIGOPS));
    result.push_back(json_spirit::Pair("sizelimit", (int64_t)block_params::MAX_BLOCK_SIZE));
    result.push_back(json_spirit::Pair("curtime", (int64_t)pblock->get_nTime()));
    result.push_back(json_spirit::Pair("bits", HexBits(pblock->get_nBits())));
    result.push_back(json_spirit::Pair("height", (int64_t)(pindexPrev->get_nHeight()+1)));

    return data.JSONRPCSuccess(result);
}

json_spirit::Value CRPCTable::submitblock(const json_spirit::Array &params, CBitrpcData &data) {
    if (data.fHelp() || params.size() < 1 || params.size() > 2) {
        return data.JSONRPCSuccess(
            "submitblock <hex data> [optional-params-obj]\n"
            "[optional-params-obj] parameter is currently ignored.\n"
            "Attempts to submit new block to network.\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");
    }

    LOCK(CRPCTable::cs_getwork);
    json_spirit::json_flags status;
    std::string hex = params[0].get_str(status);
    if(! status.fSuccess()) return data.JSONRPCError(RPC_JSON_ERROR, status.e);
    rpctable_vector blockData(strenc::ParseHex(hex));
    CDataStream ssBlock(blockData, SER_NETWORK, version::PROTOCOL_VERSION);
    CBlock block;
    try {
        ssBlock >> block;
    } catch (const std::exception &) {
        return data.JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
    }

    bool fAccepted = block_process::manage::ProcessBlock(nullptr, &block);
    if (! fAccepted)
        return data.JSONRPCSuccess("rejected");

    return data.JSONRPCSuccess(json_spirit::Value::null);
}


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

  @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_ARCH_COMMON_SIMD_FUNCTION_ROR_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_ROR_HPP_INCLUDED

#include <boost/simd/meta/cardinal_of.hpp>
#include <boost/simd/detail/assert_utils.hpp>
#include <boost/simd/function/bitwise_cast.hpp>
#include <boost/simd/function/bitwise_or.hpp>
#include <boost/simd/function/minus.hpp>
#include <boost/simd/function/shift_left.hpp>
#include <boost/simd/function/shr.hpp>
#include <boost/simd/function/splat.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/detail/dispatch/meta/scalar_of.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;

  BOOST_DISPATCH_OVERLOAD ( ror_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_< bd::unsigned_<A0>, X>
                          , bd::scalar_< bd::unsigned_<A1> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 const& a0, A1 a1) const
    {
      BOOST_ASSERT_MSG(assert_good_shift<A0>(a1), "rol : rotation is out of range");
      using s_t = bd::scalar_of_t<A0>;

      static const A1 width = sizeof(s_t)*CHAR_BIT-1;
      A1 n(a1);
      return (shift_right(a0, n)) | (shift_left(a0, (-n&width)));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ror_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_< bd::arithmetic_<A0>, X>
                          , bd::scalar_< bd::integer_<A1> >
  )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 const&  a0, A1 a1 ) const
    {
      using i_t = bd::as_integer_t<A0, unsigned>;
      using is_t = bd::as_integer_t<i_t, unsigned>;
      return bitwise_cast<A0>( ror ( bitwise_cast<i_t>(a0)
                                   , is_t(a1)
                                   )
                             );
    }
  };

  BOOST_DISPATCH_OVERLOAD_IF ( ror_
                             , (typename A0, typename A1, typename X)
                             , ( brigand::and_<
                                 detail::is_native<X>,
                                 brigand::bool_<bs::cardinal_of<A0>::value == bs::cardinal_of<A1>::value>
                                 >
                               )
                             , bd::cpu_
                             , bs::pack_< bd::unsigned_<A0>, X>
                             , bs::pack_< bd::unsigned_<A1>, X>
                             )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 const& a0, A0 const& a1) const
    {
      using s_t = bd::scalar_of_t<A0>;
      BOOST_ASSERT_MSG(assert_good_shift<A0>(a1), "ror : rotation is out of range");

      s_t static const width = sizeof(s_t)*CHAR_BIT;
      return shr(a0, a1) | shift_left(a0, (width-a1) & (width-1));
    }
  };

  BOOST_DISPATCH_OVERLOAD_IF ( ror_
                             , (typename A0, typename A1, typename X)
                             , ( brigand::and_<
                                 detail::is_native<X>,
                                 brigand::bool_<bs::cardinal_of<A0>::value == bs::cardinal_of<A1>::value>
                                 >
                               )
                               , bd::cpu_
                               , bs::pack_< bd::arithmetic_<A0>, X>
                               , bs::pack_< bd::integer_<A1>, X>
                               )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 const& a0, A1 const& a1) const
    {
      using i_t = bd::as_integer_t<A0, unsigned>;
      return bitwise_cast<A0>( ror ( bitwise_cast<i_t>(a0)
                                   , bitwise_cast<i_t>(a1)
                                   )
                             );
    }
  };
} } }

#endif


#include <boost/random.hpp>

namespace biggles { namespace detail {

/// @brief The underlying boost random number generator used by Biggles' sampling functions.
extern boost::mt19937 random_generator;

unsigned int get_random_seed();

/// @brief Reset the seed of the PRNG used by Biggles.
///
/// On startup the PRNG function used by Biggles is seeded with a value based on the system clock. This function allows
/// the seed to be set explicitly.
///
/// @param seed The new seed for the PRNG.
void seed_prng(unsigned int seed);

} } // biggles::detail


// File: train_sgd.cc
// Author: Karl Moritz Hermann (mail@karlmoritz.com)
// Created: 01-01-2013
// Last Update: Thu 03 Oct 2013 11:48:43 AM BST

// STL
#include <iostream>
#include <cmath>

// Boost
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>

#include "shared_defs.h"

// L-BFGS
#include <lbfgs.h>

// Local
#include "train_sgd.h"
#include "train_update.h"
#include "recursive_autoencoder.h"
#include "utils.h"

using namespace std;
namespace bpo = boost::program_options;

int train_sgd(Model &model, int iterations, float eta)
{
  Real* vars = nullptr;
  int number_vars = 0;

  setVarsAndNumber(vars,number_vars,model);
  cout << vars;

  float eta_t0 = eta;
  WeightVectorType theta(vars,number_vars);

  Real* dataOne = new Real[number_vars];
  Real* dataTwo = new Real[number_vars];
  Real* data1 = dataOne;
  Real* data2 = dataTwo;
  lbfgsfloatval_t error = 100000;
  lbfgsfloatval_t new_error = 100000;

  for (auto iteration = 0; iteration < iterations; ++iteration)
  {
    model.rae.debugSize(3);
    WeightVectorType grad(data1,number_vars);
    new_error = computeCostAndGrad(model,nullptr,data1,number_vars);
    cout << "Correct (error): " << new_error << " ... etc: " << eta << endl;
    cout << "Grad sum " << grad.sum();
    cout << "Theta sum " << theta.sum();

    grad *= eta;
    theta -= grad;

    if (data1 == dataOne) { data1 = dataTwo; data2 = dataOne; }
    else { data1 = dataOne; data2 = dataTwo; }

    if (new_error > error)
      eta *= 0.5;
    else
      eta *= 1.05;
    error = new_error;
    // if (eta*1000 < eta_t0)
    // eta = eta_t0; // Hack: if eta gets too small, we reset it and jump out of misery
  }

  delete [] dataOne;
  delete [] dataTwo;
  return 0;
}


//
// Created by Dominik Krupke (http://krupke.cc) on 1/11/18.
//

/*
#include <map>
#include <algorithm>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/detail/adjacency_list.hpp>
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/filtered_graph.hpp>
#include "./cyclecover/orientation_selection/solution.h"
#include "visualize.h"
#include "../problem_instance/grid_graph.h"
#include "../problem_instance/solution/simple_svg_1.0.0.hpp"

void
visualize_fractional(basic_string<char>
path, turncostcover::GridGraph &graph, std::map<apx::os::VariableIndex, double> coverage,
                     std::map<Field, double> penalty)
{

  auto max_x =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).x
                                                  < graph.GetCoordinate(
                                                      b).x;
                                            })).x;
  auto min_x =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).x
                                                  > graph.GetCoordinate(
                                                      b).x;
                                            })).x;
  auto max_y =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).y
                                                  < graph.GetCoordinate(
                                                      b).y;
                                            })).y;
  auto min_y =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).y
                                                  > graph.GetCoordinate(
                                                      b).y;
                                            })).y;

  svg::Document file{path, svg::Dimensions{2.0 + max_x - min_x, 2.0 + max_y - min_y}};

  auto field_stroke = svg::Stroke{0.05, svg::Color::Black};
  auto field_fill = svg::Fill{svg::Color{169, 169, 169}};

  for (auto vp = boost::vertices(graph); vp.first != vp.second; ++vp.first) {
    auto v = *vp.first;
    auto coord = graph.GetCoordinate(v);

    file << svg::Rectangle{
svg::Point{1.0 + coord.x - min_x, 1.0 + coord.y - min_y}, //Position
                           1, //height
                           1, //width
                           field_fill,
                           field_stroke};

  }

  std::map<Field, double> horizontal;
  std::map<Field, double> vertical;
  for (auto x: coverage) {
    auto v = x.first.f;
    if (x.first.IsStraight(graph)) {
      auto w = x.first.n2;
      if (graph.GetCoordinate(v).x != graph.GetCoordinate(w).x) {
        horizontal[v] += x.second;
      } else {
        vertical[v] += x.second;
      }
    } else {
      horizontal[v] += 0.5 * x.second;
      vertical[v] += 0.5 * x.second;
    }
  }

  for (auto vp = boost::vertices(graph); vp.first != vp.second; ++vp.first) {
    auto v = *vp.first;
    auto coord = graph.GetCoordinate(v);
    if (horizontal[v] > 0) {
      file << svg::Line{
svg::Point{1.0 + coord.x - min_x, 0.5 + coord.y - min_y},
                        svg::Point{2.0 + coord.x - min_x, 0.5 + coord.y - min_y},
                        svg::Stroke{0.1 * horizontal[v], svg::Color::Red}};
    }
    if (vertical[v] > 0) {
      file << svg::Line{
svg::Point{1.5 + coord.x - min_x, 0.0 + coord.y - min_y},
                        svg::Point{1.5 + coord.x - min_x, 1.0 + coord.y - min_y},
                        svg::Stroke{0.1 * vertical[v], svg::Color::Red}};
    }

  }
  file.save();

}
void
visualize_rounded(basic_string<char>
path, turncostcover::GridGraph &graph,
                  std::
    map<turncostcover::GridGraph::FieldId, apx::os::Orientations> orientations)
{
  auto max_x =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).x
                                                  < graph.GetCoordinate(
                                                      b).x;
                                            })).x;
  auto min_x =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).x
                                                  > graph.GetCoordinate(
                                                      b).x;
                                            })).x;
  auto max_y =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).y
                                                  < graph.GetCoordinate(
                                                      b).y;
                                            })).y;
  auto min_y =
      graph.GetCoordinate(*std::max_element(boost::vertices(graph).first,
                                            boost::vertices(graph).second,
                                            [&](const turncostcover::GridGraph::FieldId &a,
                                                const turncostcover::GridGraph::FieldId &b) {
                                              return graph.GetCoordinate(a).y
                                                  > graph.GetCoordinate(
                                                      b).y;
                                            })).y;

  svg::Document file{path, svg::Dimensions{2.0 + max_x - min_x, 2.0 + max_y - min_y}};

  auto field_stroke = svg::Stroke{0.05, svg::Color::Black};
  auto field_fill = svg::Fill{svg::Color{169, 169, 169}};

  for (auto vp = boost::vertices(graph); vp.first != vp.second; ++vp.first) {
    auto v = *vp.first;
    auto coord = graph.GetCoordinate(v);

    file << svg::Rectangle{
svg::Point{1.0 + coord.x - min_x, 1.0 + coord.y - min_y}, //Position
                           1, //height
                           1, //width
                           field_fill,
                           field_stroke};

  }

  for (auto vp = boost::vertices(graph); vp.first != vp.second; ++vp.first) {
    auto v = *vp.first;
    auto coord = graph.GetCoordinate(v);
    switch (orientations[v]) {
      case ::apx::os::Orientations::Vertical:
        file << svg::Line{
svg::Point{1.5 + coord.x - min_x, 0.0 + coord.y - min_y},
                          svg::Point{1.5 + coord.x - min_x, 1.0 + coord.y - min_y},
                          svg::Stroke{0.1, svg::Color::Red}};
        break;
      case ::apx::os::Orientations::Horizontal:
        file
                << svg::Line{
svg::Point{1.0 + coord.x - min_x, 0.5 + coord.y - min_y},
                             svg::Point{2.0 + coord.x - min_x, 0.5 + coord.y - min_y},
                             svg::Stroke{0.1, svg::Color::Red}};
        break;
      default:break;
    }
  }
  file.save();
}*/


/* Copyright (c) 2015, Julian Straub <jstraub@csail.mit.edu> Licensed
 * under the MIT license. See the license file LICENSE.
 */
#ifndef REALTIME_MF_OPENNI_HPP_INCLUDED
#define REALTIME_MF_OPENNI_HPP_INCLUDED

#include <string>

#include <pcl/io/openni_grabber.h>
#include <pcl/io/openni_camera/openni_depth_image.h>
#include <pcl/io/openni_camera/openni_image.h>

#include <Eigen/Dense>

#include <jsCore/timerLog.hpp>

#include <mmf/optimizationSO3.hpp>
#include <mmf/optimizationSO3_approx.hpp>
#include <mmf/optimizationSO3_vmf.hpp>

#include <cudaPcl/openniSmoothNormalsGpu.hpp>

#include <rtmf/rtmf.hpp>

using namespace Eigen;
using namespace std;

class RealtimeMF_openni : public cudaPcl::OpenniVisualizer
{
  public:
    RealtimeMF_openni(shared_ptr<RealtimeMF>& pRtmf);
    virtual ~RealtimeMF_openni();

    virtual void depth_cb(const uint16_t* depth, uint32_t
        w, uint32_t h);

  protected:
    virtual void visualizeRGB();

   shared_ptr<RealtimeMF> pRtmf_;
   cv::Mat normalsImg_;

//    void visualizePC()
//    {
//      if (normalsImg_.empty() || normalsImg_.rows == 0 || normalsImg_.cols
//          == 0) return;
////      cv::Mat nI (normalsImg_.rows,normalsImg_.cols, CV_8UC3);
////      //  cv::Mat nIRGB(normalsImg_.rows,normalsImg_.cols,CV_8UC3);
////      normalsImg_.convertTo(nI, CV_8UC3, 127.5,127.5);
////      cv::cvtColor(nI,nIRGB_,CV_RGB2BGR);
////      cv::imshow("normals",nIRGB_);
////      if (compress_)  cv::imshow("dcomp",normalsComp_);
////
////      if (false) {
////        // show additional diagnostics
////        std::vector<cv::Mat> nChans(3);
////        cv::split(normalsImg_,nChans);
////        cv::Mat nNans = nChans[0].clone();
////        showNans(nNans);
////        cv::imshow("normal Nans",nNans);
////        cv::Mat haveData = normalExtract->haveData();
////        cv::imshow("haveData",haveData*200);
////      }
//
//#ifdef USE_PCL_VIEWER
//      pc_ = pcl::PointCloud<pcl::PointXYZRGB>::Ptr(new
//          pcl::PointCloud<pcl::PointXYZRGB>());
//      for (uint32_t i=0; i<normalsImg_.rows; ++i)
//        for (uint32_t j=0; j<normalsImg_.cols; ++j) {
//          pcl::PointXYZRGB p;
//          p.x = normalsImg_.at<cv::Vec3f>(i,j)[0];
//          p.y = normalsImg_.at<cv::Vec3f>(i,j)[1];
//          p.z = normalsImg_.at<cv::Vec3f>(i,j)[2];
//          p.rgb = 0;
//          float norm = p.x*p.x+p.y*p.y+p.z*p.z;
//          if (0.98 <= norm && norm <= 1.02) this->pc_->push_back(p);
//        }
//      if (this->pc_->size() > 0) {
//        if(!this->viewer_->updatePointCloud(this->pc_, "pc"))
//          this->viewer_->addPointCloud(this->pc_, "pc");
//      }
//#endif
//    }
};

// -------------------------------- impl -----------------------------------

RealtimeMF_openni::RealtimeMF_openni(shared_ptr<RealtimeMF>& pRtmf)
  : pRtmf_(pRtmf)
{};

RealtimeMF_openni::~RealtimeMF_openni()
{
}

void RealtimeMF_openni::depth_cb(const uint16_t* depth, uint32_t w,
    uint32_t h)
{
  pRtmf_->compute(depth,w,h);
//  normalsImg_ = pRtmf_->normalsImg_;
//  cout<<pRtmf_->cRmf()<<endl;
  cv::Mat Iseg;
  cout<<"visualize Normals"<<endl;
  {
    //boost::mutex::scoped_lock updateLock(this->updateModelMutex);
    Iseg = pRtmf_->overlaySeg(this->rgb_);
  }
  cv::imshow("seg",Iseg);
  this->update();
}

void RealtimeMF_openni::visualizeRGB()
{
//  cv::Mat Iseg;
//  cout<<"visualize Normals"<<endl;
//  {
//    //boost::mutex::scoped_lock updateLock(this->updateModelMutex);
//    Iseg = pRtmf_->overlaySeg(this->rgb_);
//  }
//  cv::imshow("seg",Iseg);
}

#endif


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

  Program:   Visualization Toolkit
  Module:    vtkPBGLMinimumSpanningTree.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/
/*
 * Copyright (C) 2008 The Trustees of Indiana University.
 * Use, modification and distribution is subject to the Boost Software
 * License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
 */
#include "vtkPBGLMinimumSpanningTree.h"

#include "vtkBoostGraphAdapter.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkDirectedGraph.h"
#include "vtkDistributedGraphHelper.h"
#include "vtkFloatArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPBGLDistributedGraphHelper.h"
#include "vtkPBGLGraphAdapter.h"
#include "vtkPointData.h"
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStringArray.h"
#include "vtkUndirectedGraph.h"

#include <boost/graph/use_mpi.hpp>

#include <boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp>
#include <boost/graph/distributed/vertex_list_adaptor.hpp>
#include <boost/property_map/parallel/global_index_map.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/vector_property_map.hpp>
#include <boost/pending/queue.hpp>

#include <vtksys/stl/utility> // for pair
#include <vtksys/stl/iterator> // for back_inserter

using namespace boost;

vtkStandardNewMacro(vtkPBGLMinimumSpanningTree);

// Constructor/Destructor
vtkPBGLMinimumSpanningTree::vtkPBGLMinimumSpanningTree()
{
  // Default values for the origin vertex
  this->EdgeWeightArrayName = 0;
  this->OutputSelectionType = 0;
  this->SetOutputSelectionType("MINIMUM_SPANNING_TREE_EDGES");
  this->SetNumberOfInputPorts(1);
  this->SetNumberOfOutputPorts(2);
}

vtkPBGLMinimumSpanningTree::~vtkPBGLMinimumSpanningTree()
{
  this->SetEdgeWeightArrayName(0);
}

int vtkPBGLMinimumSpanningTree::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the input and output
  vtkGraph *input = vtkGraph::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkGraph *output = vtkGraph::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  // Send the data to output.
  output->ShallowCopy(input);

  vtkDistributedGraphHelper *helper = output->GetDistributedGraphHelper();
  if (!helper)
    {
    vtkErrorMacro("Distributed vtkGraph is required.");
    return 1;
    }

  // We can only deal with Parallel BGL-distributed graphs.
  vtkPBGLDistributedGraphHelper *pbglHelper
    = vtkPBGLDistributedGraphHelper::SafeDownCast(helper);
  if (!pbglHelper)
    {
    vtkErrorMacro("Can only perform parallel breadth-first-search on a Parallel BGL distributed graph");
    return 1;
    }

  // Retrieve the edge-weight array.
  if (!this->EdgeWeightArrayName)
    {
    vtkErrorMacro("Edge-weight array name is required");
    return 1;
    }
  vtkAbstractArray* abstractEdgeWeightArray
    = input->GetEdgeData()->GetAbstractArray(this->EdgeWeightArrayName);

  // Does the edge-weight array exist at all?
  if (abstractEdgeWeightArray == NULL)
    {
    vtkErrorMacro("Could not find edge-weight array named "
                  << this->EdgeWeightArrayName);
    return 1;
    }

  // Does the edge-weight array have enough values in it?
  if (abstractEdgeWeightArray->GetNumberOfTuples() < output->GetNumberOfEdges())
    {
    vtkErrorMacro("Edge-weight array named " << this->EdgeWeightArrayName
                  << " has too few values in it.");
    return 1;
    }

  bool edgeWeightArrayIsTemporary = false;
  vtkDoubleArray *edgeWeightArray
    = vtkDoubleArray::SafeDownCast(abstractEdgeWeightArray);
  if (edgeWeightArray == 0)
    {
    // Edge-weight array does not contain "double" values. We will
    // need to build a temporary array, or fail.
      if (abstractEdgeWeightArray->IsNumeric())
        {
        // Allocate a new edge-weight array.
        edgeWeightArray = vtkDoubleArray::New();
        edgeWeightArray->SetNumberOfTuples(output->GetNumberOfEdges());
        edgeWeightArrayIsTemporary = true;

        // Convert the values in the given array into doubles.
        for (vtkIdType i = 0; i < output->GetNumberOfEdges(); ++i)
          {
          vtkVariant value = abstractEdgeWeightArray->GetVariantValue(i);
          edgeWeightArray->SetTuple1(i, value.ToDouble());
          }
        }
      else
        {
        vtkErrorMacro("Edge-weight array named " << this->EdgeWeightArrayName
                      << " does not contain numeric values.")
        return 1;
        }
    }

  // Execute the algorithm
  if (vtkUndirectedGraph::SafeDownCast(output))
    {
    vtkUndirectedGraph *g = vtkUndirectedGraph::SafeDownCast(output);

    // Distributed edge-weight map
    typedef vtkDistributedEdgePropertyMapType<vtkDoubleArray>::type
      DistributedEdgeWeightMap;
    DistributedEdgeWeightMap edgeWeightMap
      = MakeDistributedEdgePropertyMap(g, edgeWeightArray);

    // Create a global index map, which takes vertex descriptors and
    // turns them into numbers in the range [0, N), where N is the
    // total number of vertices in the distributed graph.
    typedef boost::parallel::global_index_map<vtkGraphDistributedVertexIndexMap,
                                              vtkVertexGlobalMap>
      GlobalIndexMap;

    GlobalIndexMap globalIndexMap(pbglHelper->GetProcessGroup(),
                                  g->GetNumberOfVertices(),
                                  MakeDistributedVertexIndexMap(g),
                                  get(boost::vertex_global, g));

    // Run the minimum spanning tree algorithm
    std::vector<vtkEdgeType> mstEdges;
    boost::graph::distributed::boruvka_mixed_merge
      (boost::graph::make_vertex_list_adaptor(g, globalIndexMap),
       edgeWeightMap,
       std::back_inserter(mstEdges),
       globalIndexMap);

    // Select the minimum spanning tree edges.
    if (!strcmp(OutputSelectionType,"MINIMUM_SPANNING_TREE_EDGES"))
      {
      vtkSelection* sel = vtkSelection::GetData(outputVector, 1);
      vtkSmartPointer<vtkIdTypeArray> ids =
        vtkSmartPointer<vtkIdTypeArray>::New();
      vtkSmartPointer<vtkSelectionNode> node =
        vtkSmartPointer<vtkSelectionNode>::New();

      // Add the ids of each MST edge.
      for (std::vector<vtkEdgeType>::iterator i = mstEdges.begin();
           i != mstEdges.end(); ++i)
        {
        ids->InsertNextValue(i->Id);
        }

      node->SetSelectionList(ids);
      node->GetProperties()->Set(vtkSelectionNode::CONTENT_TYPE(),
                                 vtkSelectionNode::INDICES);
      node->GetProperties()->Set(vtkSelectionNode::FIELD_TYPE(),
                                 vtkSelectionNode::EDGE);
      sel->AddNode(node);
      }
    }
  else
    {
    vtkErrorMacro("Minimum spanning tree can only be computed on an undirected vtkGraph");
    return 1;
    }

  return 1;
}

void vtkPBGLMinimumSpanningTree::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "EdgeWeightArrayName: "
     << (this->EdgeWeightArrayName ? this->EdgeWeightArrayName : "(none)")
     << endl;

  os << indent << "OutputSelectionType: "
     << (this->OutputSelectionType ? this->OutputSelectionType : "(none)") << endl;
}

//----------------------------------------------------------------------------
int vtkPBGLMinimumSpanningTree::FillInputPortInformation(
  int port, vtkInformation* info)
{
  // now add our info
  if (port == 0)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGraph");
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkPBGLMinimumSpanningTree::FillOutputPortInformation(
  int port, vtkInformation* info)
{
  // now add our info
  if (port == 0)
    {
    info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkGraph");
    }
  else if (port == 1)
    {
    info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkSelection");
    }
  return 1;
}