text:
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkMapServerSource.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.

=========================================================================*/
#include "vtkMapServerSource.h"

#include "vtkPolyData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkSmartPointer.h"
#include "vtkTransform.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkNew.h"

#include <vtkIdList.h>
#include <vtkCellArray.h>
#include <vtkPoints.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkImageData.h>

#include <vtkMultisenseUtils.h>

#include <Eigen/Dense>
#include <boost/shared_ptr.hpp>
#include <boost/thread/thread.hpp>

#include <lcm/lcm-cpp.hpp>

//#include <lcmtypes/drc_lcmtypes.hpp>
#include <lcmtypes/drc/map_image_t.hpp>
#include <lcmtypes/drc/map_cloud_t.hpp>
#include <lcmtypes/drc/map_octree_t.hpp>
#include <lcmtypes/drc/map_scans_t.hpp>
#include <lcmtypes/drc/data_request_t.hpp>

#include <maps/LcmTranslator.hpp>
#include <maps/DepthImageView.hpp>
#include <maps/DepthImage.hpp>
#include <maps/PointCloudView.hpp>
#include <maps/OctreeView.hpp>
#include <maps/ScanBundleView.hpp>

#include <sys/select.h>
#include <deque>

namespace
{

const int WORKSPACE_DEPTH_VIEW_ID = drc::data_request_t::DEPTH_MAP_WORKSPACE;

/* commented out due to conflict with vtkMultisenseUtils.h version
//----------------------------------------------------------------------------
vtkSmartPointer<vtkCellArray> NewVertexCells(vtkIdType numberOfVerts)
{
  vtkNew<vtkIdTypeArray> cells;
  cells->SetNumberOfValues(numberOfVerts*2);
  vtkIdType* ids = cells->GetPointer(0);
  for (vtkIdType i = 0; i < numberOfVerts; ++i)
    {
    ids[i*2] = 1;
    ids[i*2+1] = i;
    }

  vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();
  cellArray->SetCells(numberOfVerts, cells.GetPointer());
  return cellArray;
}
*/

//----------------------------------------------------------------------------
vtkSmartPointer<vtkPolyData> PolyDataFromPointCloud(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
  vtkIdType nr_points = cloud->points.size();

  vtkNew<vtkPoints> points;
  points->SetDataTypeToFloat();
  points->SetNumberOfPoints(nr_points);

  vtkNew<vtkUnsignedCharArray> rgbArray;
  rgbArray->SetName("rgb_colors");
  rgbArray->SetNumberOfComponents(3);
  rgbArray->SetNumberOfTuples(nr_points);

  if (cloud->is_dense)
  {
    for (vtkIdType i = 0; i < nr_points; ++i) {
      float point[3] = {cloud->points[i].x, cloud->points[i].y, cloud->points[i].z};
      unsigned char color[3] = {cloud->points[i].r, cloud->points[i].g, cloud->points[i].b};
      points->SetPoint(i, point);
      rgbArray->SetTupleValue(i, color);
    }
  }
  else
  {
    vtkIdType j = 0;    // true point index
    for (vtkIdType i = 0; i < nr_points; ++i)
    {
      // Check if the point is invalid
      if (!pcl_isfinite (cloud->points[i].x) ||
          !pcl_isfinite (cloud->points[i].y) ||
          !pcl_isfinite (cloud->points[i].z))
        continue;

      float point[3] = {cloud->points[i].x, cloud->points[i].y, cloud->points[i].z};
      unsigned char color[3] = {cloud->points[i].r, cloud->points[i].g, cloud->points[i].b};
      points->SetPoint(j, point);
      rgbArray->SetTupleValue(j, color);
      j++;
    }
    nr_points = j;
    points->SetNumberOfPoints(nr_points);
    rgbArray->SetNumberOfTuples(nr_points);
  }

  vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
  polyData->SetPoints(points.GetPointer());
  polyData->GetPointData()->AddArray(rgbArray.GetPointer());
  polyData->SetVerts(NewVertexCells(nr_points));
  return polyData;
}

//----------------------------------------------------------------------------
void AddZCoordinateArray(vtkPolyData* polyData)
{
  const vtkIdType nPoints = polyData->GetNumberOfPoints();

  vtkSmartPointer<vtkDoubleArray> zcoord = vtkSmartPointer<vtkDoubleArray>::New();
  zcoord->SetName("z");
  zcoord->SetNumberOfComponents(1);
  zcoord->SetNumberOfTuples(nPoints);

  for (vtkIdType i = 0; i < nPoints; ++i)
  {
    double z = polyData->GetPoint(i)[2];
    if (!pcl_isfinite(z))
    {
      z = 0.0;
    }
    zcoord->SetValue(i, z);
  }

  polyData->GetPointData()->AddArray(zcoord);
}

//----------------------------------------------------------------------------
vtkSmartPointer<vtkPolyData> ConvertMesh(maps::TriangleMesh::Ptr mesh)
{
  const size_t nPoints = mesh->mVertices.size();
  const size_t nTris = mesh->mFaces.size();
  const bool hasNormals = (mesh->mNormals.size() != 0);

  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
  points->SetDataTypeToDouble();
  points->SetNumberOfPoints(nPoints);

  vtkSmartPointer<vtkDoubleArray> normalsArray = vtkSmartPointer<vtkDoubleArray>::New();
  normalsArray->SetName("Normals");
  normalsArray->SetNumberOfComponents(3);
  normalsArray->SetNumberOfTuples(nPoints);

  for (size_t i = 0; i < nPoints; ++i)
  {
    points->SetPoint(i, mesh->mVertices[i].data());
  }

  if (hasNormals)
  {
    for (size_t i = 0; i < nPoints; ++i)
    {
      normalsArray->SetTuple(i, mesh->mNormals[i].data());
    }
  }

  vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();

  vtkIdType ids[3];
  for (vtkIdType i = 0; i < nTris; ++i)
    {
    ids[0] = mesh->mFaces[i][0];
    ids[1] = mesh->mFaces[i][1];
    ids[2] = mesh->mFaces[i][2];
    cellArray->InsertNextCell(3, ids);
    }

  vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
  polyData->SetPoints(points);
  polyData->SetPolys(cellArray);

  if (hasNormals)
  {
    polyData->GetPointData()->SetNormals(normalsArray);
  }

  return polyData;
}

//----------------------------------------------------------------------------
class MapData
{
public:
  uint64_t Id;
  vtkSmartPointer<vtkPolyData> Data;
  vtkSmartPointer<vtkPolyData> Mesh;
  vtkSmartPointer<vtkImageData> DepthImage;
  vtkSmartPointer<vtkTransform> Transform;
};

//----------------------------------------------------------------------------
class LCMListener
{
public:

  LCMListener()
  {
    this->ShouldStop = true;
    this->NewData = false;
    this->MaxNumberOfDatasets = 20;
    this->ViewIds = vtkSmartPointer<vtkIntArray>::New();
    this->LastScanBundleUtime = 0;
    this->CurrentScanBundleId = 0;
    this->DistanceRange[0] = 0.25;
    this->DistanceRange[1] = 4.0;
    this->EdgeAngleThreshold = 30;  // degrees

    this->LCMHandle = boost::shared_ptr<lcm::LCM>(new lcm::LCM);
    if(!this->LCMHandle->good())
    {
      std::cerr <<"ERROR: lcm is not good()" <<std::endl;
    }

    this->LCMHandle->subscribe( "MAP_DEPTH", &LCMListener::depthHandler, this);
    this->LCMHandle->subscribe( "MAP_DEBUG", &LCMListener::depthHandler, this);
    this->LCMHandle->subscribe( "MAP_CLOUD", &LCMListener::cloudHandler, this);
    this->LCMHandle->subscribe( "MAP_OCTREE", &LCMListener::octreeHandler, this);
    this->LCMHandle->subscribe( "MAP_SCANS", &LCMListener::scanBundleHandler, this);
  }

  void cloudHandler(const lcm::ReceiveBuffer* rbuf, const std::string& channel, const drc::map_cloud_t* msg)
  {
    this->HandleNewData(msg);
  }

  void depthHandler(const lcm::ReceiveBuffer* rbuf, const std::string& channel, const drc::map_image_t* msg)
  {
    this->HandleNewData(msg);
  }

  void octreeHandler(const lcm::ReceiveBuffer* rbuf, const std::string& channel, const drc::map_octree_t* msg)
  {
    this->HandleNewData(msg);
  }

  void scanBundleHandler(const lcm::ReceiveBuffer* rbuf, const std::string& channel, const drc::map_scans_t* msg)
  {
    this->HandleNewData(msg);
  }

  void SetDistanceRange(double distanceRange[2])
  {
    this->DistanceRange[0] = distanceRange[0];
    this->DistanceRange[1] = distanceRange[1];
  }

  void SetEdgeAngleThreshold(double edgeAngleThreshold)
  {
    this->EdgeAngleThreshold = edgeAngleThreshold;
  }

  double GetEdgeAngleThreshold()
  {
    return this->EdgeAngleThreshold;
  }

  bool CheckForNewData()
  {
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    bool newData = this->NewData;
    this->NewData = false;
    return newData;
  }

  bool WaitForLCM(double timeout)
  {
    int lcmFd = this->LCMHandle->getFileno();

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = timeout * 1e6;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(lcmFd, &fds);

    int status = select(lcmFd + 1, &fds, 0, 0, &tv);
    if (status == -1 && errno != EINTR)
    {
      printf("select() returned error: %d\n", errno);
    }
    else if (status == -1 && errno == EINTR)
    {
      printf("select() interrupted\n");
    }
    return (status > 0 && FD_ISSET(lcmFd, &fds));
  }

  void ThreadLoopWithSelect()
  {
    while (!this->ShouldStop)
    {
      const double timeoutInSeconds = 0.3;
      bool lcmReady = this->WaitForLCM(timeoutInSeconds);

      if (this->ShouldStop)
      {
        break;
      }

      if (lcmReady)
      {
        if (this->LCMHandle->handle() != 0)
        {
          printf("lcm->handle() returned non-zero\n");
          break;
        }
      }
    }

  }

  void ThreadLoop()
  {
    while (!this->ShouldStop)
    {
      if (this->LCMHandle->handle() != 0)
      {
        printf("lcm->handle() returned non-zero\n");
        break;
      }
    }
  }

  void Start()
  {
    if (this->Thread)
      {
      return;
      }

    this->ShouldStop = false;
    this->Thread = boost::shared_ptr<boost::thread>(
      new boost::thread(boost::bind(&LCMListener::ThreadLoopWithSelect, this)));
  }

  void Stop()
  {
    if (this->Thread)
      {
      this->ShouldStop = true;
      this->Thread->interrupt();
      this->Thread->join();
      this->Thread.reset();
      }
  }

  std::vector<vtkSmartPointer<vtkPolyData> > GetDatasets(int viewId)
  {
    std::vector<vtkSmartPointer<vtkPolyData> > polyData;

    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    for (size_t i = 0; i < datasets.size(); ++i)
      {
      polyData.push_back(datasets[i].Data);
      }

    return polyData;
  }

  std::vector<double> GetTimesteps(int viewId)
  {
    boost::lock_guard<boost::mutex> lock(this->Mutex);

    std::vector<double> timesteps;
    std::deque<MapData>& datasets = this->Datasets[viewId];
    for (int i = 0; i < datasets.size(); ++i)
      {
      timesteps.push_back(i);
      }
    return timesteps;
  }

  vtkSmartPointer<vtkPolyData> GetDatasetForTime(int viewId, int timestep)
  {
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    if (timestep >= 0 && timestep < datasets.size())
      {
      return datasets[timestep].Data;
      }
    else
      {
      return 0;
      }
  }

  vtkIdType GetCurrentMapId(int viewId)
  {
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::map<int, vtkIdType>::const_iterator itr = this->CurrentMapIds.find(viewId);
    if (itr == this->CurrentMapIds.end())
      {
        return -1;
      }
    return itr->second;
  }

  void GetDataForMapId(int viewId, vtkIdType mapId, vtkPolyData* polyData)
  {
    if (!polyData)
      {
      return;
      }

    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    for (size_t i = 0; i < datasets.size(); ++i)
      {
      if (datasets[i].Id == mapId)
        {
        polyData->DeepCopy(datasets[i].Data);
        }
      }
  }

  void GetMeshForMapId(int viewId, vtkIdType mapId, vtkPolyData* polyData)
  {
    if (!polyData)
      {
      return;
      }

    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    for (size_t i = 0; i < datasets.size(); ++i)
      {
      if (datasets[i].Id == mapId)
        {
        polyData->DeepCopy(datasets[i].Mesh);
        }
      }
  }

  void GetDataForMapId(int viewId, vtkIdType mapId, vtkImageData* imageData, vtkTransform* transform)
  {
    if (!imageData || !transform)
      {
      return;
      }

    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    for (size_t i = 0; i < datasets.size(); ++i)
      {
      if (datasets[i].Id == mapId)
        {
        imageData->DeepCopy(datasets[i].DepthImage);
        transform->DeepCopy(datasets[i].Transform);
        }
      }
  }
  uint64_t GetLastScanBundleUTime()
  {
    return this->LastScanBundleUtime;
  }

  vtkIntArray* GetViewIds()
  {
    this->ViewIds->SetNumberOfTuples(this->CurrentMapIds.size());

    std::map<int, vtkIdType>::const_iterator itr;
    int i;
    for (i = 0, itr = this->CurrentMapIds.begin(); itr != this->CurrentMapIds.end(); ++itr, ++i)
      {
      this->ViewIds->SetValue(i, itr->first);
      }

    return this->ViewIds;
  }

protected:

  void UpdateDequeSize(std::deque<MapData>& datasets)
  {
    if (this->MaxNumberOfDatasets <= 0)
      {
      return;
      }
    while (datasets.size() >= this->MaxNumberOfDatasets)
      {
      datasets.pop_front();
      }
  }

  vtkIdType GetNextMapId(int viewId)
  {
    std::map<int, vtkIdType>::iterator itr = this->CurrentMapIds.find(viewId);
    if (itr == this->CurrentMapIds.end())
      {
        this->CurrentMapIds[viewId] = -1;
        itr = this->CurrentMapIds.find(viewId);
      }

    return ++itr->second;
  }

  vtkSmartPointer<vtkTransform> ToVtkTransform(const Eigen::Projective3f& mat)
  {
    vtkSmartPointer<vtkMatrix4x4> vtkmat = vtkSmartPointer<vtkMatrix4x4>::New();
    for (int i = 0; i < 4; ++i)
      {
      for (int j = 0; j < 4; ++j)
        {
        vtkmat->SetElement(i, j, mat(i,j));
        }
      }

    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->SetMatrix(vtkmat);
    return transform;
  }

  vtkSmartPointer<vtkImageData> ConvertDepthImage(std::shared_ptr<maps::DepthImage> depthImage)
  {
    int width = depthImage->getWidth();
    int height = depthImage->getHeight();

    vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();
    image->SetWholeExtent(0, width-1, 0, height-1, 0, 0);
    image->SetSpacing(1.0, 1.0, 1.0);
    image->SetOrigin(0.0, 0.0, 0.0);
    image->SetExtent(image->GetWholeExtent());
    image->SetNumberOfScalarComponents(1);
    image->SetScalarType(VTK_FLOAT);
    image->AllocateScalars();

    std::vector<float> imageData = depthImage->getData(maps::DepthImage::TypeDepth);

    /*
    float invalidValue = depthImage->getInvalidValue(maps::DepthImage::TypeDepth);
    float replacementValue = 0.0
    for (int i = 0; i < imageData.size(); ++i)
      {
      if (imageData[i] == invalidValue)
        {
          imageData[i] = replacementValue;
        }
      }
    */

    float* outPtr = static_cast<float*>(image->GetScalarPointer(0, 0, 0));
    std::copy(imageData.begin(), imageData.end(), outPtr);

    return image;
  }

  void HandleNewData(const drc::map_image_t* msg)
  {
    int viewId = msg->view_id;
    maps::DepthImageView depthImageView;
    maps::LcmTranslator::fromLcm(*msg, depthImageView);

    maps::PointCloud::Ptr pointCloud = depthImageView.getAsPointCloud();
    maps::TriangleMesh::Ptr mesh = depthImageView.getAsMesh();

    MapData mapData;
    mapData.Data = PolyDataFromPointCloud(pointCloud);
    mapData.DepthImage = this->ConvertDepthImage(depthImageView.getDepthImage());
    mapData.Mesh = ConvertMesh(mesh);
    AddZCoordinateArray(mapData.Mesh);
    mapData.Transform = this->ToVtkTransform(depthImageView.getTransform());

    //printf("storing depth map %d.  %d points.  (view id %d)\n", mapData.Id, mapData.Data->GetNumberOfPoints(), viewId);

    // store data
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    mapData.Id = this->GetNextMapId(viewId);
    datasets.push_back(mapData);
    this->UpdateDequeSize(datasets);
    this->NewData = true;
  }

  void HandleNewData(const drc::map_cloud_t* msg)
  {
    int viewId = msg->view_id;
    maps::PointCloudView cloudView;
    maps::LcmTranslator::fromLcm(*msg, cloudView);

    maps::PointCloud::Ptr pointCloud = cloudView.getAsPointCloud();

    MapData mapData;
    mapData.Data = PolyDataFromPointCloud(pointCloud);

    //printf("storing cloud map %d.  %d points.  (view id %d)\n", mapData.Id, mapData.Data->GetNumberOfPoints(), viewId);

    // store data
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    mapData.Id = this->GetNextMapId(viewId);
    datasets.push_back(mapData);
    this->UpdateDequeSize(datasets);
    this->NewData = true;
  }

  void HandleNewData(const drc::map_octree_t* msg)
  {
    int viewId = msg->view_id;
    maps::OctreeView octreeView;
    maps::LcmTranslator::fromLcm(*msg, octreeView);

    maps::PointCloud::Ptr pointCloud = octreeView.getAsPointCloud();

    MapData mapData;
    mapData.Data = PolyDataFromPointCloud(pointCloud);
    mapData.Transform = this->ToVtkTransform(octreeView.getTransform());
    mapData.Mesh = mapData.Data;

    // store data
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    mapData.Id = this->GetNextMapId(viewId);
    datasets.push_back(mapData);
    this->UpdateDequeSize(datasets);
    this->NewData = true;
  }

  void HandleNewData(const drc::map_scans_t* msg)
  {
    if (msg->utime == this->LastScanBundleUtime) return;
    this->LastScanBundleUtime = msg->utime;
    this->CurrentScanBundleId++;

    if (msg->num_scans == 0) return;

    int viewId = msg->view_id;
    maps::ScanBundleView bundleView;
    maps::LcmTranslator::fromLcm(*msg, bundleView);

    // convert/copy scans
    auto scans = bundleView.getScans();
    std::vector<ScanLineData> scanLines(scans.size());
    for (int i = 0; i < (int)scans.size(); ++i) {
      const auto& in = scans[i];
      auto& out = scanLines[i];
      out.Revolution = this->CurrentScanBundleId;
      out.ScanLineId = i;
      out.SpindleAngle = (float)i/(scans.size()-1)*180; // TODO: this is approximate at best
      out.ScanToLocalStart = in->getStartPose().cast<double>();
      out.ScanToLocalEnd = in->getEndPose().cast<double>();

      bot_core::planar_lidar_t msg;
      msg.utime = in->getTimestamp();
      msg.rad0 = in->getThetaMin();
      msg.radstep = in->getThetaStep();
      msg.ranges = in->getRanges();
      msg.nranges = msg.ranges.size();
      msg.intensities = in->getIntensities();
      if (msg.intensities.size() == 0) {
        msg.intensities.resize(msg.nranges);
        std::fill(msg.intensities.begin(), msg.intensities.end(), 0);
      }
      msg.nintensities = msg.nranges;
      out.msg = msg;
    }

    MapData mapData;
    mapData.Data = GetPointCloudFromScanLines(scanLines, this->DistanceRange, this->EdgeAngleThreshold);
    mapData.Transform = this->ToVtkTransform(bundleView.getTransform());
    mapData.Mesh = mapData.Data;

    // store data
    boost::lock_guard<boost::mutex> lock(this->Mutex);
    std::deque<MapData>& datasets = this->Datasets[viewId];
    mapData.Id = this->GetNextMapId(viewId);
    datasets.push_back(mapData);
    this->UpdateDequeSize(datasets);
    this->NewData = true;
  }

  bool NewData;
  bool ShouldStop;
  int MaxNumberOfDatasets;
  vtkIdType CurrentMapId;
  vtkSmartPointer<vtkIntArray> ViewIds;

  double EdgeAngleThreshold;
  double DistanceRange[2];

  boost::mutex Mutex;
  int64_t LastScanBundleUtime;
  int32_t CurrentScanBundleId;

  std::map<int, std::deque<MapData> > Datasets;
  std::map<int, vtkIdType> CurrentMapIds;

  boost::shared_ptr<lcm::LCM> LCMHandle;

  boost::shared_ptr<boost::thread> Thread;

};

} // end namespace

//----------------------------------------------------------------------------
class vtkMapServerSource::vtkInternal
{
public:

  vtkInternal()
  {
    this->Listener = boost::shared_ptr<LCMListener>(new LCMListener);
  }

  ~vtkInternal()
  {
  }

  boost::shared_ptr<LCMListener> Listener;
};

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkMapServerSource);

//----------------------------------------------------------------------------
vtkMapServerSource::vtkMapServerSource()
{
  this->Internal = new vtkInternal;
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
  this->DistanceRange[0] = 0.25;
  this->DistanceRange[1] = 4.0;
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
}

//----------------------------------------------------------------------------
vtkMapServerSource::~vtkMapServerSource()
{
  this->Stop();
  delete this->Internal;
}

//----------------------------------------------------------------------------
void vtkMapServerSource::Start()
{
  this->Internal->Listener->Start();
}

//----------------------------------------------------------------------------
void vtkMapServerSource::Stop()
{
  this->Internal->Listener->Stop();
}

//----------------------------------------------------------------------------
void vtkMapServerSource::Poll()
{
  if (this->Internal->Listener->CheckForNewData())
    {
    this->Modified();
    }
}

//-----------------------------------------------------------------------------
void vtkMapServerSource::SetEdgeAngleThreshold(double threshold)
{
  if (threshold == this->GetEdgeAngleThreshold())
    {
      return;
    }

  this->Internal->Listener->SetEdgeAngleThreshold(threshold);
  this->Modified();
}

//-----------------------------------------------------------------------------
double vtkMapServerSource::GetEdgeAngleThreshold()
{
  return this->Internal->Listener->GetEdgeAngleThreshold();
}

//-----------------------------------------------------------------------------
int vtkMapServerSource::GetNumberOfDatasets(int viewId)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  return this->Internal->Listener->GetDatasets(viewId).size();
}

//-----------------------------------------------------------------------------
vtkIdType vtkMapServerSource::GetCurrentMapId(int viewId)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  return this->Internal->Listener->GetCurrentMapId(viewId);
}

//-----------------------------------------------------------------------------
void vtkMapServerSource::GetDataForMapId(int viewId, vtkIdType mapId, vtkPolyData* polyData)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  this->Internal->Listener->GetDataForMapId(viewId, mapId, polyData);
}

//-----------------------------------------------------------------------------
void vtkMapServerSource::GetMeshForMapId(int viewId, vtkIdType mapId, vtkPolyData* polyData)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  this->Internal->Listener->GetMeshForMapId(viewId, mapId, polyData);
}

//-----------------------------------------------------------------------------
void vtkMapServerSource::GetDataForMapId(int viewId, vtkIdType mapId, vtkImageData* imageData, vtkTransform* transform)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  this->Internal->Listener->GetDataForMapId(viewId, mapId, imageData, transform);
}

vtkIdType vtkMapServerSource::GetLastScanBundleUTime()
{
  return this->Internal->Listener->GetLastScanBundleUTime();
}

//-----------------------------------------------------------------------------
vtkPolyData* vtkMapServerSource::GetDataset(int viewId, vtkIdType i)
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  return this->Internal->Listener->GetDatasetForTime(viewId, i);
}

//-----------------------------------------------------------------------------
vtkIntArray* vtkMapServerSource::GetViewIds()
{
  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  return this->Internal->Listener->GetViewIds();
}

//-----------------------------------------------------------------------------
int vtkMapServerSource::RequestInformation(vtkInformation *request,
                                     vtkInformationVector **inputVector,
                                     vtkInformationVector *outputVector)
{
  vtkInformation *info = outputVector->GetInformationObject(0);

  std::vector<double> timesteps = this->Internal->Listener->GetTimesteps(WORKSPACE_DEPTH_VIEW_ID);
  if (timesteps.size())
    {
    double timeRange[2] = {timesteps.front(), timesteps.back()};

    printf("time range: [%f, %f]\n", timeRange[0], timeRange[1]);

    info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &timesteps.front(), timesteps.size());
    info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2);
    }
  else
    {
    printf("no timesteps available\n");
    info->Remove(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
    info->Remove(vtkStreamingDemandDrivenPipeline::TIME_RANGE());
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkMapServerSource::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  vtkInformation *info = outputVector->GetInformationObject(0);
  vtkDataSet *output = vtkDataSet::SafeDownCast(info->Get(vtkDataObject::DATA_OBJECT()));

  int timestep = 0;
  if (info->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEPS()))
    {
    double timeRequest = info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEPS())[0];
    timestep = static_cast<int>(floor(timeRequest+0.5));
    }

  this->Internal->Listener->SetDistanceRange(this->DistanceRange);
  vtkSmartPointer<vtkPolyData> polyData = this->Internal->Listener->GetDatasetForTime(WORKSPACE_DEPTH_VIEW_ID, timestep);
  if (polyData)
    {
    output->ShallowCopy(polyData);
    }
  else
    {
    printf("no map data for timestep: %d\n", timestep);
    }

  return 1;
}

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

ext: cxx


text:
#include <iostream>
#include <dlib/matrix.h>

int main()
{
    // declare a matrix
    dlib::matrix<double, 3, 1> y;
    dlib::matrix<double, 3, 3> M;
    // initialize matrices
    M = 54.2, 65.2, 43,
        23.4, 12.3, 55.4,
        11, 34.6, 78.9;
    y = 3.5,
        1.2,
        6.7;
    // solve this linear system
    dlib::matrix<double> x = dlib::inv(M)*y;
    std::cout << "x: \n" << x << "\n";
    // check if the calculation is correct
    std::cout << "Check: M*x- y= \n" << M*x - y << "\n";

    // sum the matrix
    double sumM = 0;
    for (int r = 0; r < M.nr(); ++r)
    {
        for (int c = 0; c < M.nc(); ++c)
        {
            sumM += M(r, c);
        }
    }
    std::cout << "Sum of matrix by looping= " << sumM << "\n";
    std::cout << "Sum of matrix by sum() function= " << dlib::sum(M) << "\n";
    // print using comma separator
    std::cout <<"print matrix M using comma delimiter: \n" <<dlib::csv << M << "\n";

    std::system("pause");
    return 0;
}

ext: cpp

max_stars_repo_name: juxiangwu/image-processing

max_issues_repo_name: juxiangwu/image-processing

max_forks_repo_name: juxiangwu/image-processing


text:
// Copyright (c) 2019 The Stealth Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iterator>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

#include "QPRegistry.hpp"

#include "txdb-leveldb.h"

using namespace std;
using namespace json_spirit;

extern bool fDebugQPoS;
extern bool fDebugBlockCreation;
extern Value ValueFromAmount(int64_t amount);

QPRegistry::QPRegistry()
{
    SetNull();
}

QPRegistry::QPRegistry(const QPRegistry *const pother)
{
    Copy(pother);
}

void QPRegistry::SetNull()
{
    nVersion = QPRegistry::CURRENT_VERSION;
    nRound = 0;
    nRoundSeed = 0;
    mapStakers.clear();
    mapBalances.clear();
    mapLastClaim.clear();
    mapActive.clear();
    mapAliases.clear();
    bRecentBlocks.reset();
    nIDCounter = 0;
    nIDSlotPrev = 0;
    fCurrentBlockWasProduced = false;
    // can't penalize the very first staker ever to have a slot
    fPrevBlockWasProduced = true;
    nBlockHeight = 0;
    hashBlock = (fTestNet ? chainParams.hashGenesisBlockTestNet :
                            hashGenesisBlock);
    hashBlockLastSnapshot = hashBlock;
    hashLastBlockPrev1Queue = hashBlock;
    hashLastBlockPrev2Queue = hashBlock;
    hashLastBlockPrev3Queue = hashBlock;
    nHeightExitedReplay = 0;
    queue.SetNull();
    queuePrev.SetNull();
    powerRoundPrev.SetNull();
    powerRoundCurrent.SetNull();
    fShouldRollback = false;
}

unsigned int QPRegistry::GetRound() const
{
    return nRound;
}

uint32_t QPRegistry::GetRoundSeed() const
{
    return nRoundSeed;
}

unsigned int QPRegistry::Size() const
{
    return mapStakers.size();
}

bool QPRegistry::IsInReplayMode() const
{
    return !nHeightExitedReplay;
}

bool QPRegistry::OutOfReplayMode() const
{
    return (bool)nHeightExitedReplay;
}

int QPRegistry::GetBlockHeight() const
{
    return nBlockHeight;
}

uint256 QPRegistry::GetBlockHash() const
{
    return hashBlock;
}

const QPQueue* QPRegistry::GetQueue() const
{
    return &queue;
}

uint256 QPRegistry::GetHashLastBlockPrev1Queue() const
{
    return hashLastBlockPrev1Queue;
}

uint256 QPRegistry::GetHashLastBlockPrev2Queue() const
{
    return hashLastBlockPrev2Queue;
}

uint256 QPRegistry::GetHashLastBlockPrev3Queue() const
{
    return hashLastBlockPrev3Queue;
}

int64_t QPRegistry::GetTotalEarned() const
{
    int64_t total = 0;
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        total += it->second.GetTotalEarned();
    }
    return total;
}

unsigned int QPRegistry::GetNumberOf(bool (QPStaker::* f)() const) const
{
    unsigned int count = 0;
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if ((it->second.*f)())
        {
            count += 1;
        }
    }
    return count;
}

unsigned int QPRegistry::GetNumberProductive() const
{
    return GetNumberOf(&QPStaker::IsProductive);
}

unsigned int QPRegistry::GetNumberEnabled() const
{
    return GetNumberOf(&QPStaker::IsEnabled);
}

unsigned int QPRegistry::GetNumberDisabled() const
{
    return GetNumberOf(&QPStaker::IsDisabled);
}

unsigned int QPRegistry::GetNumberQualified() const
{
    return GetNumberOf(&QPStaker::IsQualified);
}

unsigned int QPRegistry::GetNumberDisqualified() const
{
    return GetNumberOf(&QPStaker::IsDisqualified);
}

bool QPRegistry::IsEnabledStaker(unsigned int nStakerID) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return false;
    }
    return pstaker->IsEnabled();
}

bool QPRegistry::CanEnableStaker(unsigned int nStakerID, int nHeight) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return false;
    }
    return pstaker->CanBeEnabled(nHeight);
}

bool QPRegistry::IsQualifiedStaker(unsigned int nStakerID) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return false;
    }
    return pstaker->IsQualified();
}

bool QPRegistry::TimestampIsValid(unsigned int nStakerID,
                                  unsigned int nTime) const
{
    bool fIsValid = true;
    QPWindow window;
    if (!queue.GetWindowForID(nStakerID, window))
    {
        fIsValid = error("TimestampIsValid(): invalid staker ID");
    }
    else if (nTime < window.start)
    {
        fIsValid = error("TimestampIsValid(): timestamp too early for staker");
    }
    else if (nTime > window.end)
    {
        fIsValid = error("TimestampIsValid(): timestamp too late for staker");
    }
    else if (nTime > GetAdjustedTime())
    {
        fIsValid = error("TimestampIsValid(): qPoS block timestamp in future");
    }
    if (!fIsValid)
    {
        unsigned int slot;
        queue.GetSlotForID(nStakerID, slot);
        if (fDebugQPoS)
        {
            printf("TimestampIsValid(): staker: %u, staker_slot: %u\n"
                   "  staker_window: %u-%u, time: %u, round: %u, seed: %u\n"
                   "  Queue: %s\n",
                   nStakerID, slot, window.start, window.end, nTime,
                   nRound, nRoundSeed, queue.ToString().c_str());
        }
    }
    return fIsValid;
}

unsigned int QPRegistry::GetQueueMinTime() const
{
    return queue.GetMinTime();
}

unsigned int QPRegistry::GetCurrentSlot() const
{
    return queue.GetCurrentSlot();
}

unsigned int QPRegistry::GetCurrentSlotStart() const
{
    return queue.GetCurrentSlotStart();
}

unsigned int QPRegistry::GetCurrentSlotEnd() const
{
    return queue.GetCurrentSlotEnd();
}

bool QPRegistry::TimeIsInCurrentSlotWindow(unsigned int nTime) const
{
    return queue.TimeIsInCurrentSlotWindow(nTime);
}

unsigned int QPRegistry::GetCurrentID() const
{
    return queue.GetCurrentID();
}

unsigned int QPRegistry::GetCurrentQueueSize() const
{
    return queue.Size();
}

unsigned int QPRegistry::GetPreviousQueueSize() const
{
    return queuePrev.Size();
}

void QPRegistry::GetSlotsInfo(int64_t nTime,
                              unsigned int nSlotFirst,
                              const QPQueue& q,
                              vector<QPSlotInfo> &vRet) const
{
    vRet.clear();
    for (unsigned int slot = nSlotFirst; slot < q.Size(); ++slot)
    {
        vRet.push_back(QPSlotInfo(nTime, slot, q));
    }
}

void QPRegistry::GetCurrentSlotsInfo(int64_t nTime,
                                     unsigned int nSlotFirst,
                                     vector<QPSlotInfo> &vRet) const
{
    return GetSlotsInfo(nTime, nSlotFirst, queue, vRet);
}

void QPRegistry::GetPreviousSlotsInfo(int64_t nTime,
                                      unsigned int nSlotFirst,
                                      vector<QPSlotInfo> &vRet) const
{
    return GetSlotsInfo(nTime, nSlotFirst, queuePrev, vRet);
}

bool QPRegistry::CurrentBlockWasProduced() const
{
    return fCurrentBlockWasProduced;
}

unsigned int QPRegistry::GetRecentBlocksSize() const
{
    return bRecentBlocks.size();
}

int QPRegistry::GetRecentBlock(unsigned int n) const
{
    if (n >= bRecentBlocks.size())
    {
        return -1;
    }
    return bRecentBlocks[n];
}

unsigned int QPRegistry::GetCurrentIDCounter() const
{
    return nIDCounter;
}

unsigned int QPRegistry::GetNextIDCounter() const
{
    return nIDCounter + 1;
}

bool QPRegistry::GetBalanceForPubKey(const CPubKey &key,
                                     int64_t &nBalanceRet) const
{
    map<CPubKey, int64_t>::const_iterator it = mapBalances.find(key);
    if (it == mapBalances.end())
    {
        return error("GetBalanceForPubKey(): pubkey not found");
    }
    nBalanceRet = it->second;
    return true;
}

bool QPRegistry::PubKeyIsInLedger(const CPubKey &key) const
{
    return (mapBalances.count(key) != 0);
}

bool QPRegistry::PubKeyActiveCount(const CPubKey &key, int &nCountRet) const
{
    map<CPubKey, int>::const_iterator it = mapActive.find(key);
    if (it == mapActive.end())
    {
        return error("PubKeyActiveCount(): pubkey not found");
    }
    nCountRet = it->second;
    return true;
}

bool QPRegistry::PubKeyIsInactive(const CPubKey &key, bool &fInActiveRet) const
{
    map<CPubKey, int>::const_iterator it = mapActive.find(key);
    if (it == mapActive.end())
    {
        return error("PubKeyIsInactive(): pubkey not found");
    }
    fInActiveRet = (it->second <= 0);
    return true;
}

bool QPRegistry::CanClaim(const CPubKey &key,
                          int64_t nValue,
                          int64_t nClaimTime) const
{
    int64_t nTime = GetAdjustedTime();
    // need to specify time as an input for blockchain sync/validation
    if (nClaimTime > nTime)
    {
        return error("CanClaim(): can't predict future");
    }
    if (nClaimTime <= 0)
    {
        nClaimTime = nTime;
    }
    map<CPubKey, int64_t>::const_iterator it = mapBalances.find(key);
    if (it == mapBalances.end())
    {
        return error("CanClaim(): no such balance in ledger");
    }
    if (nValue > it->second)
    {
        return error("CanClaim(): claim exceeds balance");
    }
    it = mapLastClaim.find(key);
    if (it != mapLastClaim.end())
    {
        if ((!fTestNet) && (nClaimTime < (it->second + QP_MIN_SECS_PER_CLAIM)))
        {
            return error("CanClaim(): too soon to claim");
        }
    }
    return true;
}

bool QPRegistry::GetOwnerKey(unsigned int nStakerID, CPubKey &keyRet) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return error("GetOwnerKey(): no such staker %u", nStakerID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("GetOwnerKey(): staker is disqualified %u", nStakerID);
    }
    keyRet = pstaker->pubkeyOwner;
    return true;
}

bool QPRegistry::GetManagerKey(unsigned int nStakerID, CPubKey &keyRet) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return error("GetManagerKey(): no such staker %u", nStakerID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("GetManagerKey(): staker is disqualified %u", nStakerID);
    }
    keyRet = pstaker->pubkeyManager;
    return true;
}

bool QPRegistry::GetDelegateKey(unsigned int nStakerID, CPubKey &keyRet) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return error("GetDelegateKey(): no such staker %u", nStakerID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("GetDelegateKey(): staker is disqualified %u", nStakerID);
    }
    keyRet = pstaker->pubkeyDelegate;
    return true;
}

bool QPRegistry::GetControllerKey(unsigned int nStakerID,
                                  CPubKey &keyRet) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return error("GetControllerKey(): no such staker %u", nStakerID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("GetControllerKey(): staker is disqualified %u",
                     nStakerID);
    }
    keyRet = pstaker->pubkeyController;
    return true;
}

bool QPRegistry::GetStakerWeight(unsigned int nStakerID,
                           unsigned int &nWeightRet) const
{
    const QPStaker* pstaker = GetStaker(nStakerID);
    if (!pstaker)
    {
        return error("GetKeyForID() : no staker for ID %d", nStakerID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("GetStakerWeight(): staker is disqualified %u",
                     nStakerID);
    }
    // sanity assertion, should never fail
    assert ((nIDCounter + 1) > nStakerID);
    // higher number equates to more seniority
    unsigned int nSeniority = (nIDCounter + 1) - nStakerID;
    nWeightRet = pstaker->GetWeight(nSeniority);
    return true;
}

void QPRegistry::AsJSON(Object &objRet) const
{
    objRet.clear();
    Array aryQueue;
    for (unsigned int slot = 0; slot < queue.Size(); ++slot)
    {
        unsigned int nID;
        queue.GetIDForSlot(slot, nID);
        QPWindow w;
        queue.GetWindowForSlot(slot, w);
        Object objSlot;
        objSlot.push_back(Pair("id", static_cast<int64_t>(nID)));
        objSlot.push_back(Pair("start_time", static_cast<int64_t>(w.start)));
        objSlot.push_back(Pair("end_time", static_cast<int64_t>(w.end)));

        QPSlotStatus status;
        queue.GetStatusForSlot(slot, status);
        string strStatus(GetSlotStatusType(status));
        objSlot.push_back(Pair("status", strStatus));
        aryQueue.push_back(objSlot);
    }
    unsigned int nCurrentSlot = queue.GetCurrentSlot();

    vector<pair<unsigned int, const QPStaker*> > vIDs;
    QPRegistryConstIterator mit;
    for (mit = mapStakers.begin(); mit != mapStakers.end(); ++mit)
    {
        if (mit->second.IsQualified())
        {
            vIDs.push_back(make_pair(mit->first, &(mit->second)));
        }
    }

    sort(vIDs.begin(), vIDs.end());

    Object objStakers;
    vector<pair<unsigned int, const QPStaker*> >::const_iterator vit;
    for (vit = vIDs.begin(); vit != vIDs.end(); ++vit)
    {
        Object objStkr;
        unsigned int nSeniority = (nIDCounter + 1) - vit->first;
        vit->second->AsJSON(vit->first, nSeniority, objStkr);
        string sID = to_string(vit->first);
        objStakers.push_back(Pair(sID, objStkr));
    }

    map<CPubKey, int64_t>::const_iterator kit;
    Object objBalances;
    for (kit = mapBalances.begin(); kit != mapBalances.end(); ++kit)
    {
        CPubKey key = kit->first;
        int64_t amt = kit->second;

        Object objBal;

        objBal.push_back(Pair("balance", ValueFromAmount(amt)));

        map<CPubKey, int64_t>::const_iterator it;
        it = mapLastClaim.find(key);
        if (it != mapLastClaim.end())
        {
            objBal.push_back(Pair("last_claim", it->second));
        }

        map<CPubKey, int>::const_iterator jt;
        jt = mapActive.find(key);
        if (jt != mapActive.end())
        {
            objBal.push_back(Pair("active_count",
                                  static_cast<int64_t>(jt->second)));
        }
        objBalances.push_back(Pair(HexStr(key.Raw()), objBal));
    }

    objRet.push_back(Pair("version", nVersion));
    objRet.push_back(Pair("round", static_cast<int64_t>(nRound)));
    objRet.push_back(Pair("round_seed", static_cast<int64_t>(nRoundSeed)));
    objRet.push_back(Pair("block_height", nBlockHeight));
    objRet.push_back(Pair("block_hash", hashBlock.GetHex()));
    objRet.push_back(Pair("last_block_hash_prev_1_queue",
                           hashLastBlockPrev1Queue.GetHex()));
    objRet.push_back(Pair("last_block_hash_prev_2_queue",
                           hashLastBlockPrev2Queue.GetHex()));
    objRet.push_back(Pair("last_block_hash_prev_3_queue",
                           hashLastBlockPrev3Queue.GetHex()));
    objRet.push_back(Pair("in_replay", IsInReplayMode()));
    objRet.push_back(Pair("should_roll_back", fShouldRollback));
    objRet.push_back(Pair("counter_next",
                          static_cast<int64_t>(nIDCounter + 1)));
    objRet.push_back(Pair("recent_blocks",
                          BitsetAsHex(bRecentBlocks)));
    objRet.push_back(Pair("current_block_was_produced",
                          fCurrentBlockWasProduced));
    objRet.push_back(Pair("prev_block_was_produced",
                          fPrevBlockWasProduced));
    objRet.push_back(Pair("current_slot",
                          static_cast<int64_t>(nCurrentSlot)));
    objRet.push_back(Pair("pico_power", GetPicoPower()));
    objRet.push_back(Pair("prev_pico_power", GetPicoPowerPrev()));
    objRet.push_back(Pair("current_pico_power", GetPicoPowerCurrent()));
    objRet.push_back(Pair("queue", aryQueue));
    objRet.push_back(Pair("queue_blocks", HexStr(queue.GetBlockStats())));
    objRet.push_back(Pair("queue_summary", queue.ToString()));
    objRet.push_back(Pair("stakers", objStakers));
    objRet.push_back(Pair("balances", objBalances));
}

void QPRegistry::GetStakerAsJSON(unsigned int nID,
                                 Object &objRet,
                                 bool fWithRecentBlocks) const
{
    const QPStaker* pstaker = GetStaker(nID);
    if (pstaker)
    {
        unsigned int nSeniority = pstaker->IsDisqualified() ?
                                              0 : (nIDCounter + 1) - nID;
        unsigned int nNftID = 0;
        if (mapNftOwners.count(nID))
        {
            nNftID = mapNftOwners.at(nID);
        }
        pstaker->AsJSON(nID, nSeniority, objRet, fWithRecentBlocks, nNftID);
    }
    else
    {
        objRet.clear();
    }
}

void QPRegistry::CheckSynced()
{
    if (IsInReplayMode())
    {
        int nTime = GetAdjustedTime();
        if (HasEnoughPower() && queue.TimeIsInCurrentSlotWindow(nTime))
        {
            printf("QPRegistry::CheckSynced(): exiting replay mode\n");
            ExitReplayMode();
        }
    }
}

void QPRegistry::Copy(const QPRegistry *const pother)
{
    nVersion = pother->nVersion;
    nRound = pother->nRound;
    nRoundSeed = pother->nRoundSeed;
    mapStakers = pother->mapStakers;
    mapBalances = pother->mapBalances;
    mapLastClaim = pother->mapLastClaim;
    mapActive = pother->mapActive;
    mapAliases = pother->mapAliases;
    queue = pother->queue;
    queuePrev = pother->queuePrev;
    bRecentBlocks = pother->bRecentBlocks;
    nIDCounter = pother->nIDCounter;
    nIDSlotPrev = pother->nIDSlotPrev;
    fCurrentBlockWasProduced = pother->fCurrentBlockWasProduced;
    fPrevBlockWasProduced = pother->fPrevBlockWasProduced;
    nBlockHeight = pother->nBlockHeight;
    hashBlock = pother->hashBlock;
    hashBlockLastSnapshot = pother->hashBlockLastSnapshot;
    hashLastBlockPrev1Queue = pother->hashLastBlockPrev1Queue;
    hashLastBlockPrev2Queue = pother->hashLastBlockPrev2Queue;
    hashLastBlockPrev3Queue = pother->hashLastBlockPrev3Queue;
    powerRoundPrev = pother->powerRoundPrev;
    powerRoundCurrent = pother->powerRoundCurrent;
    mapNftOwners = pother->mapNftOwners;
    mapNftOwnerLookup = pother->mapNftOwnerLookup;

    nHeightExitedReplay = pother->nHeightExitedReplay;
    fShouldRollback = pother->fShouldRollback;
}

void QPRegistry::ActivatePubKey(const CPubKey &key,
                                const CBlockIndex* pindex)
{
    if (!mapActive.count(key))
    {
        mapActive[key] = 0;
    }
    if (mapActive[key] == 0)
    {
        mapBalances[key] = 0;
        mapLastClaim[key] = pindex->nTime;
    }
    mapActive[key] += 1;
}

bool QPRegistry::DeactivatePubKey(const CPubKey &key)
{
    map<CPubKey, int>::iterator it = mapActive.find(key);
    if (it == mapActive.end())
    {
        return error("DeactivatePubKey(): pubkey not found");
    }
    if (it->second < 1)
    {
        return error("DeactivatePubKey(): Can't deactivate inactive key");
    }
    it->second -= 1;
    return true;
}

bool QPRegistry::SetStakerAlias(unsigned int nID, const string &sAlias)
{
    string sKey;
    if (!AliasIsAvailable(sAlias, sKey))
    {
        return false;
    }
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        return error("SetStakerAlias(): no staker with ID %u", nID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("SetStakerAlias(): staker %u is disqualified", nID);
    }
    if (!pstaker->SetAlias(sAlias))
    {
        return error("SetStakerAlias(): can't set to %s", sAlias.c_str());
    }
    mapAliases[sKey] = make_pair(nID, sAlias);
    return true;
}

bool QPRegistry::NftIsAvailable(const unsigned int nID) const
{
    // FIXME: this test can be removed after XST_FORKPURCHASE3
    if (GetFork(nBlockHeight) < XST_FORKPURCHASE3)
    {
        return (mapNfts.count(nID) && !mapNftOwners.count(nID));
    }
    else
    {
        return (mapNfts.count(nID) && !mapNftOwnerLookup.count(nID));
    }
}

bool QPRegistry::NftIsAvailable(const unsigned int nID,
                                string &sCharKeyRet) const
{
    sCharKeyRet.clear();
    // FIXME: this test can be removed after XST_FORKPURCHASE3
    if (GetFork(nBlockHeight) < XST_FORKPURCHASE3)
    {
        if (mapNfts.count(nID) && !mapNftOwners.count(nID))
        {
            sCharKeyRet = mapNfts[nID].strCharKey;
        }
    }
    else
    {
        if (mapNfts.count(nID) && !mapNftOwnerLookup.count(nID))
        {
            sCharKeyRet = mapNfts[nID].strCharKey;
        }
    }
    return (bool)sCharKeyRet.size();
}

bool QPRegistry::NftIsAvailable(const string sCharKey,
                                unsigned int& nIDRet) const
{
    nIDRet = 0;
    // FIXME: this can be removed after testing
    if (sCharKey != ToLowercaseSafe(sCharKey))
    {
        return error("NftIsAvailable(): TSNH alias is not lowercase");
    }
    if (mapNftLookup.count(sCharKey))
    {
        nIDRet = mapNftLookup[sCharKey];
    }
    if (mapNftOwnerLookup.count(nIDRet))
    {
        nIDRet = 0;
    }
    return (bool)nIDRet;
}

unsigned int QPRegistry::GetNftOwner(const unsigned int nID) const
{
    unsigned int nStakerID = 0;
    if (mapNftOwnerLookup.count(nID))
    {
        nStakerID = mapNftOwnerLookup.at(nID);
    }
    return nStakerID;
}

bool QPRegistry::GetNftIDForAlias(const string &sAlias,
                                  unsigned int& nIDRet) const
{
    string sKey = ToLowercaseSafe(sAlias);
    if (mapNftLookup.count(sKey))
    {
        nIDRet = mapNftLookup[sKey];
    }
    else
    {
        nIDRet = 0;
    }
    return (bool)nIDRet;
}

bool QPRegistry::GetNftNickForID(const unsigned int nID,
                                  string &sNickRet) const
{
    if (mapNfts.count(nID))
    {
        sNickRet = mapNfts[nID].strNickname;
    }
    else
    {
        sNickRet = "";
    }
    return (bool)sNickRet.size();
}

const QPStaker* QPRegistry::GetStaker(unsigned int nID) const
{
    if ((nID > 0) && (nID <= nIDCounter))
    {
        QPRegistryConstIterator iter = mapStakers.find(nID);
        if (iter != mapStakers.end())
        {
            return &(iter->second);
        }
    }
    return NULL;
}

const QPStaker* QPRegistry::GetNewestStaker() const
{
    return GetStaker(nIDCounter);
}

void QPRegistry::GetEnabledStakers(vector<const QPStaker*> &vRet) const
{
    vRet.clear();
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if (it->second.IsEnabled())
        {
            vRet.push_back(&(it->second));
        }
    }
}

void QPRegistry::GetDisabledStakers(vector<const QPStaker*> &vRet) const
{
    vRet.clear();
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if (it->second.IsDisabled())
        {
            vRet.push_back(&(it->second));
        }
    }
}

void QPRegistry::GetStakers(vector<const QPStaker*> &vRet) const
{
    vRet.clear();
    for (unsigned int n = 1; n <= nIDCounter; ++n)
    {
        QPRegistryConstIterator it = mapStakers.find(n);
        if (it == mapStakers.end())
        {
            // this should never happen: no such ID
            printf("GetStakers(): TSNH No such ID %u\n", n);
        }
        else
        {
            vRet.push_back(&(it->second));
        }
    }
}

void QPRegistry::GetStakers(QPMapPStakers &mapRet) const
{
    mapRet.clear();
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        mapRet[it->first] = &(it->second);
    }
}

unsigned int QPRegistry::GetIDForCurrentSlot() const
{
    if (queue.Size() == 0)
    {
        return 0;
    }
    return queue.GetCurrentID();
}

uint64_t QPRegistry::GetPicoPower() const
{
    // will not overflow for a long time
    //    e.g. if staker net blocks is 1 billion (633 yr with just 4 stakers):
    //       4 * sqrt(1e9) = 126491.10
    //       126491.10 * 1e12 = 1.265e+17
    //       (2**64 - 1) / 1.265e+17 = 145.8 > 1
    //    eg. 500 yr with 30 stakers: 59.95 > 1
    uint64_t w = static_cast<uint64_t>(powerRoundPrev.GetWeight() +
                                       powerRoundCurrent.GetWeight());
    uint64_t t = static_cast<uint64_t>(powerRoundPrev.GetTotalWeight() +
                                       powerRoundCurrent.GetTotalWeight());
    if (t == 0)
    {
        return 0;
    }
    return (w * TRIL) / t;
}

uint64_t QPRegistry::GetPicoPowerPrev() const
{
    uint64_t w = static_cast<uint64_t>(powerRoundPrev.GetWeight());
    uint64_t t = static_cast<uint64_t>(powerRoundPrev.GetTotalWeight());
    if (t == 0)
    {
        return 0;
    }
    return (w * TRIL) / t;
}

uint64_t QPRegistry::GetPicoPowerCurrent() const
{
    uint64_t w = static_cast<uint64_t>(powerRoundCurrent.GetWeight());
    uint64_t t = static_cast<uint64_t>(powerRoundCurrent.GetTotalWeight());
    if (t == 0)
    {
        return 0;
    }
    return (w * TRIL) / t;
}

bool QPRegistry::HasEnoughPower() const
{
    return (powerRoundPrev.IsEmpty() ||
            (GetPicoPower() >= QP_MIN_PICO_POWER));
}

bool QPRegistry::ShouldRollback() const
{
    return fShouldRollback;
}

void QPRegistry::GetCertifiedNodes(vector<string> &vNodesRet) const
{
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if (it->second.IsDisqualified())
        {
            continue;
        }
        string sValue;
        if (it->second.GetMeta(META_KEY_CERTIFIED_NODE, sValue))
        {
            vNodesRet.push_back(sValue);
        }
    }
}

bool QPRegistry::IsCertifiedNode(const string &sNodeAddress) const
{
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if (it->second.IsDisqualified())
        {
            continue;
        }
        string sValue;
        if (it->second.GetMeta(META_KEY_CERTIFIED_NODE, sValue))
        {
            if (sValue == sNodeAddress)
            {
                return true;
            }
        }
    }
    return false;
}

// returns true if the current block should be produced
// this is the key procedure for keeping the registry
//   synced when not in replay
bool QPRegistry::GetIDForCurrentTime(CBlockIndex *pindex,
                                     unsigned int &nIDRet,
                                     unsigned int &nTimeRet)
{
    nTimeRet = static_cast<unsigned int>(GetAdjustedTime());

    // queue has gotten ahead some how
    if (nTimeRet < queue.GetCurrentSlotStart())
    {
        // this should never happen
        printf("GetIDForCurrentTime(): TSNH queue has gotten ahead\n");
        nIDRet = queue.GetCurrentID();
        return false;
    }

    if (nTimeRet <= queue.GetMaxTime())
    {
        unsigned int nSlot;
        if (queue.GetSlotForTime(nTimeRet, nSlot))
        {
            nIDRet = queue.GetCurrentID();
            unsigned int nCurrentSlot = queue.GetCurrentSlot();
            // return the current ID if the queue is up to date
            if (nSlot == nCurrentSlot)
            {
                return !fCurrentBlockWasProduced;
            }
            else if (nSlot < nCurrentSlot)
            {
                printf("GetIDForCurrentTime(): queue is ahead of time\n");
                return false;
            }
        }
        else
        {
            // this should never happen
            printf("GetIDForCurrentTime(): TSNH queue state is inconsistent\n");
            nIDRet = 0;
            return false;
        }
    }

    // Apparently there is a need to advance the registry,
    // which only happens when blocks are connected.
    // Here, advance a tempRegistry as if it were the real registry
    // to determine which ID should sign the next block.
    // Using a tempRegistry allows for reorganization in the case
    // of missing blocks, where they suddenly arrive and are on a chain
    // with greater trust.
    QPRegistry *pregistryTemp = new QPRegistry(this);
    if (fDebugBlockCreation)
    {
        printf("GetIDForCurrentTime(): advancing temp registry at %d\n"
               "   this height=%d, current_slot: %u\n"
               "   temp height=%d, current_slot: %u\n",
               pindex->nHeight,
               this->nBlockHeight,
               this->GetCurrentSlot(),
               pregistryTemp->GetBlockHeight(),
               pregistryTemp->GetCurrentSlot());
    }
    // don't take snapshots of the temp registry
    pregistryTemp->UpdateOnNewTime(nTimeRet, pindex, false);
    nIDRet = pregistryTemp->GetIDForCurrentSlot();
    delete pregistryTemp;
    return true;
}

unsigned int QPRegistry::GetIDForPrevSlot() const
{
    return nIDSlotPrev;
}

bool QPRegistry::GetPrevRecentBlocksMissedMax(unsigned int nID,
                                              uint32_t &nMaxRet) const
{
    static const uint32_t MAX_STDDEVS = 8;
    uint32_t n = 0;
    uint32_t total = 0;
    vector<uint32_t> vMissed;
    QPRegistryConstIterator it;
    for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
    {
        if (it->first == nID)
        {
            continue;
        }
        QPStaker staker = it->second;
        if (staker.IsEnabled())
        {
            n += 1;
            uint32_t m = staker.GetPrevRecentBlocksMissed();
            vMissed.push_back(m);
            total += m;
        }
    }
    if (n == 0)
    {
        return error("GetPrevRecentBlocksMissedMax(): 0 enabled stakers");
    }
    uint32_t mean = total / n;
    uint32_t sum_of_delta_sq = 0;
    vector<uint32_t>::const_iterator jt;
    for (jt = vMissed.begin(); jt != vMissed.end(); ++jt)
    {
        uint32_t m = *jt;
        uint32_t delta = (mean > m) ? (mean - m) : (m - mean);
        sum_of_delta_sq += delta * delta;
    }
    uint32_t stdev = uisqrt(sum_of_delta_sq / n);
    nMaxRet = max(QP_MIN_OF_BLOCKS_MISSED_MAX, mean + (stdev * MAX_STDDEVS));
    return true;
}

bool QPRegistry::AliasIsAvailable(const string &sAlias,
                                  string &sKeyRet) const
{
    if (!AliasIsValid(sAlias))
    {
        printf("AliasIsAvailable(): not valid: %s\n", sAlias.c_str());
        return false;
    }
    sKeyRet = ToLowercaseSafe(sAlias);
    if (mapAliases.count(sKeyRet) > 0)
    {
        printf("AliasIsAvailable(): alias exists: %s\n", sAlias.c_str());
        return false;
    }
    return true;
}

bool QPRegistry::GetIDForAlias(const string &sAlias,
                               unsigned int &nIDRet) const
{
    string sKey = ToLowercaseSafe(sAlias);
    map<string, pair<unsigned int, string> >::const_iterator it;
    it = mapAliases.find(sKey);
    if (it == mapAliases.end())
    {
        return false;
    }
    nIDRet = it->second.first;
    return true;
}

bool QPRegistry::GetAliasForID(unsigned int nID,
                               string &sAliasRet) const
{
    const QPStaker* pstaker = GetStaker(nID);
    if (!pstaker)
    {
        return error("GetAliasForID(): no such staker");
    }
    sAliasRet = pstaker->GetAlias();
    return true;
}

string QPRegistry::GetAliasForID(unsigned int nID)
{
    if (nID == 0)
    {
        return STRING_NO_ALIAS;
    }
    const QPStaker* pstaker = GetStaker(nID);
    if (!pstaker)
    {
        printf("GetAliasForID(): ERROR: no such staker");
        return STRING_NO_ALIAS;
    }
    return pstaker->GetAlias();
}

QPStaker* QPRegistry::GetStakerForID(unsigned int nID)
{
    if ((nID > 0) && (nID <= nIDCounter))
    {
        QPMapStakers::iterator iter = mapStakers.find(nID);
        if (iter != mapStakers.end())
        {
            return &(iter->second);
        }
    }
    return NULL;
}

QPStaker* QPRegistry::GetStakerForAlias(const string &sAlias)
{
    unsigned int nID;
    if (!GetIDForAlias(sAlias, nID))
    {
        return NULL;
    }
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        printf("GetStakerForAlias(): alias and ID mismatch\n");
        return NULL;
    }
    return pstaker;
}

// this will only be called for a block that is being added to the end of the
// chain so that the Registry state is synchronous with the block
// same goes for StakerMissedBlock
bool QPRegistry::StakerProducedBlock(const CBlockIndex *pindex,
                                     int64_t nReward)
{
    unsigned int nID = pindex->nStakerID;
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        return error("StakerProducedBlock(): unknown ID %d", nID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("StakerProducedBlock(): TSNH staker %d disqualified", nID);
    }
    pstaker = &mapStakers[nID];
    unsigned int nSeniority = (nIDCounter + 1) - nID;
    powerRoundCurrent.PushBack(nID, pstaker->GetWeight(nSeniority), true);
    int64_t nOwnerReward, nDelegateReward;
    pstaker->ProducedBlock(pindex->phashBlock,
                           nReward,
                           fPrevBlockWasProduced,
                           nOwnerReward,
                           nDelegateReward);
    if (fTestNet && (GetFork(nBlockHeight) < XST_FORKMISSFIX))
    {
        mapBalances[pstaker->pubkeyOwner] += nOwnerReward;
    }
    else
    {
        mapBalances[pstaker->pubkeyManager] += nOwnerReward;
    }
    if (nDelegateReward > 0)
    {
        mapBalances[pstaker->pubkeyDelegate] += nDelegateReward;
    }
    if (GetFork(nBlockHeight) < XST_FORKREINSTATE)
    {
        DisqualifyStakerIfNecessary(nID, pstaker);
    }
    bRecentBlocks <<= 1;
    bRecentBlocks[0] = true;
    fCurrentBlockWasProduced = true;
    fPrevBlockWasProduced = true;
    return true;
}

bool QPRegistry::StakerMissedBlock(unsigned int nID, int nHeight)
{
    int nFork = GetFork(nHeight);
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        return error("StakerMissedBlock(): unknown ID %d", nID);
    }
    if (pstaker->IsDisqualified())
    {
        return error("StakerMissedBlock(): TSNH staker %d disqualified", nID);
    }
    unsigned int nSeniority = (nIDCounter + 1) - nID;
    powerRoundCurrent.PushBack(nID, pstaker->GetWeight(nSeniority), false);
    pstaker->MissedBlock(fPrevBlockWasProduced, nFork);
    if (OutOfReplayMode() && fDebugBlockCreation)
    {
        printf("StakerMissedBlock(): staker=%s, round=%d, seed=%u, slot=%d, "
               "window=%d-%d, picopower=%" PRIu64 ", registryheight=%d\n",
               GetAliasForID(nID).c_str(),
               nRound,
               nRoundSeed,
               queue.GetCurrentSlot(),
               queue.GetCurrentSlotStart(),
               queue.GetCurrentSlotEnd(),
               GetPicoPower(),
               nBlockHeight);
    }
    if (nFork >= XST_FORKMISSFIX)
    {
        DisableStakerIfNecessary(nID, pstaker, nHeight);
    }
    bRecentBlocks <<= 1;
    fPrevBlockWasProduced = false;
    return true;
}

bool QPRegistry::DisableStaker(unsigned int nID, int nHeight)
{
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        return error("DisableStaker(): unknown ID");
    }
    if (pstaker->IsDisabled())
    {
        return error("DisableStaker(): staker already disabled");
    }
    pstaker->Disable(nHeight);
    return true;
}

bool QPRegistry::DisableStakerIfNecessary(unsigned int nID,
                                          const QPStaker* pstaker,
                                          int nHeight)
{
    bool fResult = true;
    // auto disable kicks in with feeless transactions
    if (GetFork(nBlockHeight) < XST_FORKFEELESS)
    {
        return true;
    }
    if (pstaker->ShouldBeDisabled(nHeight))
    {
        // want at least 3 enabled stakers in times of disaster
        if (GetNumberEnabled() > 3)
        {
            fResult = DisableStaker(nID, nHeight);
        }
    }
    return fResult;
}

bool QPRegistry::DisqualifyStaker(unsigned int nID)
{
    QPStaker* pstaker = GetStakerForID(nID);
    if (!pstaker)
    {
        return error("DisqualifyStaker(): unknown ID");
    }
    if (pstaker->IsDisqualified())
    {
        return error("DisqualifyStaker(): staker already disqualified");
    }
    pstaker->Disqualify();
    return true;
}

bool QPRegistry::DisqualifyStakerIfNecessary(unsigned int nID,
                                             const QPStaker* pstaker)
{
    bool fResult = true;
    if (fTestNet)
    {
        return true;
    }
    uint32_t nPrevRecentBlocksMissedMax;
    if (!GetPrevRecentBlocksMissedMax(nID, nPrevRecentBlocksMissedMax))
    {
        fResult = false;
    }
    if (pstaker->ShouldBeDisqualified(nPrevRecentBlocksMissedMax))
    {
        fResult = DisqualifyStaker(nID);
    }
    return fResult;
}

bool QPRegistry::NewQueue(unsigned int nTime0, const uint256& prevHash)
{
    vector<unsigned int> vIDs;
    QPRegistryConstIterator iter;
    for (iter = mapStakers.begin(); iter != mapStakers.end(); ++iter)
    {
        if (iter->second.IsEnabled())
        {
            vIDs.push_back(iter->first);
        }
    }
    if (vIDs.size() < 1)
    {
        return error("NewQueue(): No qualified stakers");
    }
    sort(vIDs.begin(), vIDs.end());
    uint256 hash(prevHash);
    if (fTestNet)
    {
        hash = SerializeHash(hash);
    }
    else
    {
        for (int i = 0; i < QP_ROUNDS; ++i)
        {
           hash = Hash9(hash.begin(), hash.end());
        }
    }
    valtype vch;
    for (unsigned int i = 0; i < 4; ++i)
    {
        vch.push_back(*(hash.begin() + i));
    }
    uint32_t seed = static_cast<uint32_t>(vchnum(vch).GetValue());
    boost::mt19937 gen(seed);
    boost::uniform_int<> dist;
    QPShuffler shuffler(gen, dist);

    // cannot count on gcc STL to have standardized random_shuffle
    typedef vector<uint32_t>::iterator vi_type;
    vi_type first = vIDs.begin();
    vi_type last = vIDs.end();
    if (first == last)
    {
        return error("NewQueue(): TSNH No qualified stakers");
    }
    for (vi_type i = first + 1; i != last; ++i)
    {
        vi_type j = first + shuffler((i - first) + 1);
        if (i != j)
        {
            iter_swap(i, j);
        }
    }

    queuePrev = queue;
    queue = QPQueue(nTime0, vIDs);
    nRound += 1;
    nRoundSeed = seed;
    powerRoundPrev.Copy(powerRoundCurrent);
    powerRoundCurrent.SetNull();
    hashLastBlockPrev3Queue = hashLastBlockPrev2Queue;
    hashLastBlockPrev2Queue = hashLastBlockPrev1Queue;
    hashLastBlockPrev1Queue = prevHash;
    return true;
}

unsigned int QPRegistry::IncrementID()
{
    nIDCounter += 1;
    return nIDCounter;
}

bool QPRegistry::DockInactiveKeys(int64_t nMoneySupply)
{
    int64_t nDockValue = nMoneySupply / DOCK_INACTIVE_FRACTION;
    bool fResult = true;
    map<CPubKey, int>::const_iterator it;
    for (it = mapActive.begin(); it != mapActive.end(); ++it)
    {
        if (it->second < 1)
        {
            map<CPubKey, int64_t>::iterator jt = mapBalances.find(it->first);
            if (jt == mapBalances.end())
            {
                fResult = error("DockInactivKeys(): %s missing in balances",
                                HexStr(it->first.Raw()).c_str());
            }
            else
            {
                jt->second -= nDockValue;
            }
        }
    }
    return fResult;
}

void QPRegistry::PurgeLowBalances(int64_t nMoneySupply)
{
    int64_t nDockValue = nMoneySupply / DOCK_INACTIVE_FRACTION;
    vector<CPubKey> vPurge;
    map<CPubKey, int64_t>::const_iterator it;
    for (it = mapBalances.begin(); it != mapBalances.end(); ++it)
    {
        int number = 0;
        if (mapActive.count(it->first))
        {
            number = mapActive[it->first];
        }
        if ((it->second < nDockValue) && (number <= 0))
        {
            vPurge.push_back(it->first);
        }
    }
    vector<CPubKey>::const_iterator jt;
    for (jt = vPurge.begin(); jt != vPurge.end(); ++jt)
    {
        mapBalances.erase(*jt);
        mapActive.erase(*jt);
    }
}

bool QPRegistry::UpdateOnNewTime(unsigned int nTime,
                                 const CBlockIndex *const pindex,
                                 int nSnapshotType,
                                 bool fWriteLog)
{
    static const unsigned int N = BLOCKS_PER_SNAPSHOT * RECENT_SNAPSHOTS;
    // blocks per sparse snapshot
    static const unsigned int M = BLOCKS_PER_SNAPSHOT *
                                  PERMANENT_SNAPSHOT_RATIO;

    int nHeight = pindex->nHeight;
    int nFork = GetFork(nHeight);

    bool fWriteSnapshot = true;
    bool fEraseSnapshot = false;
    if ((nSnapshotType == QPRegistry::NO_SNAPS) ||
        (nFork < XST_FORKPURCHASE))
    {
        fWriteSnapshot = false;
    }
    else if (nSnapshotType == QPRegistry::SPARSE_SNAPS)
    {
        fWriteSnapshot = ((nHeight % M) == 0);
        fEraseSnapshot = !fWriteSnapshot;
    }
    else
    {
        // all snaps
        fWriteSnapshot = ((nHeight % BLOCKS_PER_SNAPSHOT) == 0);
    }

    uint256 hash = *(pindex->phashBlock);

    fWriteSnapshot = fWriteSnapshot && (hash != hashBlockLastSnapshot);

    CTxDB txdb;
    fEraseSnapshot = fEraseSnapshot && txdb.RegistrySnapshotIsViable(nHeight);

    // take snapshot here because the registry should be fully caught up
    // with the best (previous) block
    if (fWriteSnapshot)
    {
        hashBlockLastSnapshot = hash;
        txdb.WriteRegistrySnapshot(nHeight, *this);

        unsigned int nStakerSlot = 0;
        queue.GetSlotForID(pindex->nStakerID, nStakerSlot);

        printf("UpdateOnNewTime(): writing snapshot at %d\n"
               "   hash=%s\n"
               "   height=%d, time=%" PRId64 ", "
                  "staker_id=%d, staker_slot=%d\n"
               "   round=%d, seed=%u, window=%d-%d, picopower=%" PRIu64 "\n"
               "   %s\n",
               nHeight,
               hash.ToString().c_str(),
               nHeight,
               pindex->GetBlockTime(),
               pindex->nStakerID,
               nStakerSlot,
               nRound,
               nRoundSeed,
               queue.GetCurrentSlotStart(),
               queue.GetCurrentSlotEnd(),
               GetPicoPower(),
               queue.ToString().c_str());

        int nHeightErase = nHeight - N;
        if (nHeightErase % M != 0)
        {
            txdb.EraseRegistrySnapshot(nHeightErase);
        }
    }
    else if (fEraseSnapshot)
    {
        txdb.EraseRegistrySnapshot(nHeight);
    }

    if (GetFork(nHeight + 1) >= XST_FORKQPOS)
    {
        unsigned int nNewQueues = 0;
        if (queue.IsEmpty())
        {
            // qPoS shall begin
            printf("UpdateOnNewTime(): Starting qPoS at block\n   %s\n",
                   hash.ToString().c_str());
            if (!NewQueue(pindex->nTime + 1, hash))
            {
                return error("UpdateOnNewTime(): New queue failed.");
            }
            nNewQueues += 1;
        }
        while (queue.GetCurrentSlotEnd() < nTime)
        {
            if (!fCurrentBlockWasProduced)
            {
                // After FORKFEELESS2, don't dock stakers for blocks
                //    after the first new queue here because by the 2nd
                //    queue there has already been a complete queue
                //    without a block and this could only mean that the
                //    chain had halted.
                if ((nFork >= XST_FORKQPOS) &&
                    ((nFork < XST_FORKFEELESS2) || (nNewQueues <= 1)))
                {
                    StakerMissedBlock(queue.GetCurrentID(), nHeight);
                }
                else
                {
                    ExitReplayMode();
                }
            }
            if (!queue.IncrementSlot())
            {
                DockInactiveKeys(pindex->nMoneySupply);
                PurgeLowBalances(pindex->nMoneySupply);
                if (!NewQueue(queue.GetMaxTime() + 1, hashBlock))
                {
                    return error("UpdateOnNewTime(): New queue failed.");
                }
                nNewQueues += 1;
            }
            fCurrentBlockWasProduced = false;
            if (HasEnoughPower())
            {
                if (fShouldRollback)
                {
                    fShouldRollback = false;
                }
            }
            else
            {
                if (!fShouldRollback)
                {
                    fShouldRollback = true;
                }
                if (OutOfReplayMode())
                {
                    ExitReplayMode();
                }
            }
        }
        if (nNewQueues > 0)
        {
            if (fWriteLog || fDebugQPoS)
            {
                printf("UpdateOnNewTime(): %u new queues with %s\n",
                       nNewQueues, hashBlock.ToString().c_str());
                printf("   newest queue starts at %d\n", queue.GetMinTime());
            }
            if (fDebugQPoS)
            {
                printf("  Queue: %s\n", queue.ToString().c_str());
            }
        }
    }

    return true;
}

bool QPRegistry::UpdateOnNewBlock(const CBlockIndex *const pindex,
                                  int nSnapshotType,
                                  bool fWriteLog,
                                  bool fJustCheck)
{
    const CBlockIndex *pindexPrev = (pindex->pprev ? pindex->pprev : pindex);

    int nFork = GetFork(pindex->nHeight);

    if (GetFork(nBlockHeight) < XST_FORKMISSFIX2)
    {
        // Chain halting destroyed missed and docked block stats, so
        //    give everyone a fresh start on docked blocks.
        if (nFork >= XST_FORKMISSFIX2)
        {
            QPRegistryIterator iter;
            for (iter = mapStakers.begin(); iter != mapStakers.end(); ++iter)
            {
                iter->second.ResetDocked();
            }
        }
        // Unfortunately, a line that disqualified stakers accidentally
        // remained in a previous commit, so now we have to undo the damage.
        else if ((GetFork(nBlockHeight) < XST_FORKREINSTATE) &&
                 (nFork >= XST_FORKREINSTATE))
        {
            QPRegistryIterator iter;
            for (iter = mapStakers.begin(); iter != mapStakers.end(); ++iter)
            {
                QPStaker& staker = iter->second;
                if (staker.IsDisqualified())
                {
                    staker.Requalify(true);
                }
                staker.ResetDocked();
            }
        }
     }

    // Q: Why do we update on new time before updating the registry
    //    for the new block?
    // A: Because we can't advance the queue until we have an event (block)
    //    that says to what time we should advance the queue.
    if (!UpdateOnNewTime(pindex->nTime, pindexPrev, nSnapshotType, fWriteLog))
    {
        return error("UpdateOnNewBlock(): could not update on new time for %s",
                     pindex->phashBlock->ToString().c_str());
    }
    if (nFork >= XST_FORKPURCHASE)
    {
        if (!pindex->vDeets.empty())
        {
            if (fWriteLog)
            {
                printf("UpdateOnNewBlock(): apply ops for block %d\n",
                       pindex->nHeight);
            }
            int nCode = ApplyOps(pindex);
            if (nCode != 0)
            {
                printf("UpdateOnNewBlock(): apply ops fail %d\n", nCode);
                if (!fJustCheck)
                {
                    printf("UpdateOnNewBlock(): no good way to continue\n");
                    exit(0);
                }
                return false;
            }
        }
    }
    if (nFork >= XST_FORKQPOS)
    {
        unsigned int nStakerSlot = 0;
        if (!queue.GetSlotForID(pindex->nStakerID, nStakerSlot))
        {
            return error("UpdateOnNewBlock(): no such staker ID %u",
                         pindex->nStakerID);
        }

        if (fWriteLog || fDebugQPoS)
        {
            printf("UpdateOnNewBlock(): hash=%s\n"
                   "   height=%d, time=%" PRId64 ", "
                   "staker_id=%d, staker_slot=%d\n"
                   "   round=%d, seed=%u, window=%d-%d, picopower=%" PRIu64 "\n"
                   "   %s\n",
                   pindex->phashBlock->ToString().c_str(),
                   pindex->nHeight,
                   pindex->GetBlockTime(),
                   pindex->nStakerID,
                   nStakerSlot,
                   nRound,
                   nRoundSeed,
                   queue.GetCurrentSlotStart(),
                   queue.GetCurrentSlotEnd(),
                   GetPicoPower(),
                   queue.ToString().c_str());
        }

        if (nStakerSlot != queue.GetCurrentSlot())
        {
            printf("UpdateOnNewBlock(): hash=%s\n"
                   "   height=%d, time=%" PRId64 ", "
                   "staker_id=%d, staker_slot=%d\n"
                   "   round=%d, seed=%u, window=%d-%d, picopower=%" PRIu64 "\n"
                   "   %s\n",
                   pindex->phashBlock->ToString().c_str(),
                   pindex->nHeight,
                   pindex->GetBlockTime(),
                   pindex->nStakerID,
                   nStakerSlot,
                   nRound,
                   nRoundSeed,
                   queue.GetCurrentSlotStart(),
                   queue.GetCurrentSlotEnd(),
                   GetPicoPower(),
                   queue.ToString().c_str());

            return error("UpdateOnNewBlock(): staker slot (%d) "
                         "is not current slot (%d)",
                         nStakerSlot,
                         queue.GetCurrentSlot());
        }

        if (fCurrentBlockWasProduced && OutOfReplayMode())
        {
            return error("UpdateOnNewBlock(): "
                         "block already produced for this slot");
        }
        int64_t nReward = GetQPoSReward(pindex->pprev);
        if (!StakerProducedBlock(pindex, nReward))
        {
            return error("UpdateOnNewBlock(): no staker with ID %u",
                         pindex->nStakerID);
        }
        if (queue.SlotProduced(nStakerSlot) == 0)
        {
            // this should never happen because the slot was already checked
            return error("UpdateOnNewBlock(): TSNH no such slot %u",
                         nStakerSlot);
        }
        QPMapStakers::iterator it;
        for (it = mapStakers.begin(); it != mapStakers.end(); ++it)
        {
            if (it->second.IsQualified())
            {
                it->second.SawBlock();
            }
        }
    }
    nBlockHeight = pindex->nHeight;
    hashBlock = *(pindex->phashBlock);
    return true;
}

// this is where new stakers are born
bool QPRegistry::ApplyPurchase(const QPTxDetails &deet,
                               const CBlockIndex* pindex)
{

    if (deet.keys.empty())
    {
        return error("ApplyPurchase(): no keys");
    }

    QPStaker staker(deet);

    unsigned int nKeys = deet.keys.size();

    if (deet.t == TX_PURCHASE1)
    {
        if (nKeys != 1)
        {
            return error("ApplyPurchase(): not 1 key");
        }
    }
    else if (deet.t == TX_PURCHASE4)
    {
        if (nKeys == 4)
        {
            staker.pubkeyManager = deet.keys[1];
            staker.pubkeyDelegate = deet.keys[2];
            staker.pubkeyController = deet.keys[3];
        }
        else if (nKeys == 3)
        {
            staker.pubkeyManager = deet.keys[1];
            staker.pubkeyDelegate = deet.keys[1];
            staker.pubkeyController = deet.keys[2];
        }
        else
        {
            return error("ApplyPurchase(): not 3 or 4 keys");
        }
        if (!staker.SetDelegatePayout(deet.pcm))
        {
            return error("ApplyPurchase(): bad payout");
        }
    }
    else
    {
        // this should never happen
        return error("ApplyPurchase(): TSNH not a purchase");
    }
    string sKey;
    if (!AliasIsAvailable(deet.alias, sKey))
    {
        return error("ApplyPurchase(): alias not valid");
    }
    unsigned int nID = IncrementID();
    mapStakers[nID] = staker;

    // if not 0, then id specifies an NFT
    if (deet.id)
    {
        string sKeyUnused;
        if (!NftIsAvailable(deet.id, sKeyUnused))
        {
            // this should rarely happen
            return error("ApplyPurchase(): NFT isn't available (by ID)");
        }
        if (sKeyUnused != sKey)
        {
            // this should never happen
            return error("ApplyPurchase(): TSNH staker and NFT alias mismatch");
        }
        if (!SetStakerNft(nID, deet.id))
        {
            // this should never happen
            return error("ApplyPurchase(): TSNH couldn't set staker NFT (id)");
        }
        if (!mapStakers[nID].SetAlias(mapNfts[deet.id].strNickname))
        {
            return error("ApplyPurchase(): TSNH staker can't set alias to nick");
        }
    }
    else if (mapNftLookup.count(sKey))
    {
        unsigned int nNftID;
        if (!NftIsAvailable(sKey, nNftID))
        {
            return error("ApplyPurchase(): NFT isn't available (by key)");
        }
        if (!SetStakerNft(nID, nNftID))
        {
            // this should never happen
            return error("ApplyPurchase(): TSNH couldn't set staker NFT (key)");
        }
        if (!mapStakers[nID].SetAlias(mapNfts[nNftID].strNickname))
        {
            return error("ApplyPurchase(): TSNH can't set staker alias to nick");
        }
    }
    else
    {
        // grab next nft
        QPMapNftIterator it;
        for (it = mapNfts.begin(); it != mapNfts.end(); ++it)
        {
            if (!mapNftOwnerLookup.count(it->first))
            {
                break;
            }
        }
        if (it == mapNfts.end())
        {
            // this should never happen
            return error("ApplyPurchase(): TSNH no NFTs available");
        }
        if (!SetStakerNft(nID, it->first))
        {
            // this should never happen
            return error("ApplyPurchase(): TSNH couldn't set next NFT");
        }
        // user took next available character, so gets to choose staker alias
        if (!mapStakers[nID].SetAlias(deet.alias))
        {
            return error("ApplyPurchase(): TSNH staker can't set alias");
        }
    }

    if (fTestNet)
    {
        if (GetFork(pindex->nHeight) < XST_FORKMISSFIX)
        {
            ActivatePubKey(staker.pubkeyOwner, pindex);
        }
    }
    ActivatePubKey(staker.pubkeyManager, pindex);
    // for purposes of counting references, it's necessary to activate
    // the delegate even if it isn't getting a payout
    ActivatePubKey(staker.pubkeyDelegate, pindex);

    mapAliases[sKey] = make_pair(nID, deet.alias);
    return true;
}

bool QPRegistry::ApplySetKey(const QPTxDetails &deet,
                             const CBlockIndex* pindex)
{
    if (!mapStakers.count(deet.id))
    {
        return error("ApplySetKey(): no such staker");
    }
    if (mapStakers[deet.id].IsDisqualified())
    {
        return error("ApplySetKey(): staker is disqualified");
    }
    if (deet.keys.size() != 1)
    {
        return error("ApplySetKey(): wrong number of keys");
    }
    int nFork = GetFork(pindex->nHeight);
    QPStaker* pstaker = &mapStakers[deet.id];
    const CPubKey keyNew = deet.keys[0];
    switch (deet.t)
    {
    case TX_SETOWNER:
      {
        const CPubKey keyOld = pstaker->pubkeyOwner;
        if (keyNew != keyOld)
        {
            if (fTestNet)
            {
                // testnet hack for vestigal activated owner keys:
                //    don't fail if the old key wasn't active
                if (!DeactivatePubKey(keyOld))
                {
                    printf("ApplySetKey(): old testnet owner not active");
                }
                if (nFork < XST_FORKMISSFIX)
                {
                    ActivatePubKey(keyNew, pindex);
                }
            }
            pstaker->pubkeyOwner = keyNew;
        }
        break;
      }
    case TX_SETMANAGER:
      {
        const CPubKey keyOld = pstaker->pubkeyManager;
        if (keyNew != keyOld)
        {
            if (!DeactivatePubKey(keyOld))
            {
                return error("ApplySetKey(): TSNH old manager not active");
            }
            ActivatePubKey(keyNew, pindex);
        }
        pstaker->pubkeyManager = keyNew;
        break;
      }
    case TX_SETDELEGATE:
      {
        const CPubKey keyOld = pstaker->pubkeyDelegate;
        if (!pstaker->SetDelegatePayout(deet.pcm))
        {
            return error("ApplySetKey(): bad payout");
        }
        if (keyNew != keyOld)
        {
            if (!DeactivatePubKey(keyOld))
            {
                return error("ApplySetKey(): TSNH old delegate not activated");
            }
            ActivatePubKey(keyNew, pindex);
            pstaker->pubkeyDelegate = keyNew;
        }
        break;
      }
    case TX_SETCONTROLLER:
        pstaker->pubkeyController = keyNew;
        break;
    default:
        // should never happen
        return error("ApplySetKey(): TSNH Not a setkey");
    }
    return true;
}

bool QPRegistry::ApplySetState(const QPTxDetails &deet, int nHeight)
{
    if (!mapStakers.count(deet.id))
    {
        return error("ApplySetState(): no such staker");
    }
    QPStaker& staker = mapStakers[deet.id];
    if (staker.IsDisqualified())
    {
        return error("ApplySetState(): staker is disqualified");
    }
    if (deet.t == TX_ENABLE)
    {
        if (staker.CanBeEnabled(nBlockHeight))
        {
            if (!staker.Enable())
            {
                return error("ApplySetState(): can't enable");
            }
        }
        else
        {
           // enabling too soon is a no-op
           printf("ApplySetState(): too soon to enable, ignoring\n");
        }
    }
    else if (deet.t == TX_DISABLE)
    {
        staker.Disable(nHeight);
    }
    else
    {
        return error("ApplySetState(): not a setstate");
    }
    return true;
}

bool QPRegistry::ApplyClaim(const QPTxDetails &deet, int64_t nBlockTime)
{
    if (deet.t != TX_CLAIM)
    {
        return error("ApplyClaim(): not a claim");
    }
    if (deet.keys.size() != 1)
    {
        return error("ApplyClaim(): not 1 key");
    }
    CPubKey key = deet.keys[0];
    if (!CanClaim(key, deet.value, nBlockTime))
    {
        return error("ApplyClaim(): key %s can't claim %" PRIu64,
                     HexStr(key.Raw()).c_str(), deet.value);
    }
    mapLastClaim[key] = nBlockTime;
    mapBalances[key] -= deet.value;
    return true;
}

bool QPRegistry::ApplySetMeta(const QPTxDetails &deet)
{
    if (deet.t != TX_SETMETA)
    {
        return error("ApplySetMeta(): not a setmeta");
    }
    if (!mapStakers.count(deet.id))
    {
        return error("ApplySetMeta(): no such staker");
    }
    if (mapStakers[deet.id].IsDisqualified())
    {
        return error("ApplySetMeta(): staker is disqualified");
    }
    mapStakers[deet.id].SetMeta(deet.meta_key, deet.meta_value);
    return true;
}

bool QPRegistry::SetStakerNft(unsigned int nStakerID,
                              unsigned int nNftID)
{
    if (!mapStakers.count(nStakerID))
    {
        return error("SetStakerNft(): no such staker");
    }
    if (!mapNfts.count(nNftID))
    {
        return error("SetStakerNft(): no such NFT");
    }
    if (mapNftOwners.count(nStakerID))
    {
        return error("SetStakerNft(): staker already assigned NFT");
    }
    if (mapNftOwnerLookup.count(nNftID))
    {
        return error("SetStakerNft(): NFT already assigned to staker");
    }
    mapNftOwners[nStakerID] = nNftID;
    mapNftOwnerLookup[nNftID] = nStakerID;
    return true;
}

// unless just checking, any failed ops terminate with an assertion by
//    caller because there is no good way to continue
// TODO: make return an enum
int QPRegistry::ApplyOps(const CBlockIndex *pindex)
{
    BOOST_FOREACH(const QPTxDetails &deet, pindex->vDeets)
    {
        switch (deet.t)
        {
        case TX_PURCHASE1:
        case TX_PURCHASE4:
            if (!ApplyPurchase(deet, pindex))
            {
                return 1;
            }
            break;
        case TX_SETOWNER:
        case TX_SETMANAGER:
        case TX_SETDELEGATE:
        case TX_SETCONTROLLER:
            if (!ApplySetKey(deet, pindex))
            {
                return 2;
            }
            break;
        case TX_ENABLE:
        case TX_DISABLE:
            if (!ApplySetState(deet, pindex->nHeight))
            {
                return 3;
            }
            break;
        case TX_CLAIM:
            if (!ApplyClaim(deet, pindex->nTime))
            {
                return 4;
            }
            break;
        case TX_SETMETA:
            if (!ApplySetMeta(deet))
            {
                return 5;
            }
            break;
        default:
            return 6;
        }
    }
    return 0;
}

// This is not ideal. Registry should decide for itself.
void QPRegistry::EnterReplayMode()
{
    if (OutOfReplayMode())
    {
        // wait for enough blocks to roll in to advance the queue
        //   before going back into replay
        if (nBlockHeight > (int)(nHeightExitedReplay + (1 + queue.Size())))
        {
            printf("QPRegistry::EnterReplayMode(): entering replay mode\n");
            nHeightExitedReplay = 0;
        }
    }
}

void QPRegistry::ExitReplayMode()
{
    // Explict exit of replay is only necessary to kickstart
    // block production (typically not needed on mainnet).
    nHeightExitedReplay = nBlockHeight;
}

max_stars_repo_name: sotblad/Stealth

max_issues_repo_name: sotblad/Stealth

max_forks_repo_name: sotblad/Stealth


text:
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>

#include <boost/array.hpp>
#include <boost/filesystem.hpp>
#include <chrono>
#include <iostream>
#include <regex>

#include "http_server.hpp"
#include "nlohmann/json.hpp"

namespace ip = boost::asio::ip;
using json = nlohmann::json;

void Print(const boost::system::error_code &ec,
           boost::asio::steady_timer *timer, int *count) {
    if (*count < 3) {
        std::cout << "[" << *count << "] "
                  << "Hello world!" << std::endl;
        ++(*count);
        timer->expires_at(timer->expires_at() + std::chrono::seconds(1));
        timer->async_wait(
            std::bind(&Print, std::placeholders::_1, timer, count));
    }
}

#define BUF_SIZE 1024
void Datalink(boost::asio::ip::tcp::socket sock) {
    while (true) {
        boost::array<char, BUF_SIZE> data;

        boost::system::error_code ec;
        std::size_t length = sock.read_some(boost::asio::buffer(data), ec);

        if (ec == boost::asio::error::eof)
            break;
        else if (ec) {
            throw boost::system::system_error(ec);
        }
        // boost::asio::write(sock, boost::asio::buffer(data, length));
        sock.write_some(boost::asio::buffer(data, length));
    }
}

int main(int argc, char *argv[]) {
    auto web_root_path = boost::filesystem::canonical("../static");
    auto path = boost::filesystem::canonical(web_root_path / "index.html");
    auto ifs = std::make_shared<std::ifstream>();
    ifs->open(path.string(),
              std::ifstream::in | std::ios::binary | std::ios::ate);

    if (*ifs) {
        auto length = ifs->tellg();
        std::cout << "Len " << length << std::endl;
        ifs->seekg(0, std::ios::beg);
        boost::asio::streambuf sb;
        ifs->get(sb, '\0');

        boost::asio::streambuf::const_buffers_type bufs = sb.data();
        std::string file_content(boost::asio::buffers_begin(bufs),
                                 boost::asio::buffers_end(bufs));
        std::cout << file_content << std::endl;
    }
    // std::regex r("^/user/([0-9]+)/article/([a-zA-Z0-9]+)$");
    std::regex r("^/(.*)$");
    std::smatch match_result;
    std::string source_str("/user/19977/article/ebacff07");
    std::cout << std::regex_match(source_str, r) << std::endl;
    std::regex_match(source_str, match_result, r);
    for (auto s : match_result) {
        std::cout << s << std::endl;
    }
    // return 0;
    int port = 8000;
    if (argc < 2)
        std::cout << "Using default port 8000" << std::endl;
    else
        port = std::atoi(argv[1]);

    boost::asio::io_service io_ctx;

    // boost::asio::steady_timer timer(io_ctx, std::chrono::seconds(3));
    // int count = 0;
    // timer.async_wait( std::bind(&Print, std::placeholders::_1, &timer,
    // &count));

    ip::tcp::endpoint tcp_endpoint(ip::tcp::v4(), port);
    ip::tcp::acceptor tcp_acceptor(io_ctx, tcp_endpoint);
    tcp_acceptor.listen();

    auto mime = std::map<std::string, std::string>();
    mime[std::string(".html")] = std::string("text/html");
    mime[std::string(".js")] = std::string("application/json");
    std::cout << "MIME done" << std::endl;
    std::cout << mime.find(std::string(".html"))->second << std::endl;

    std::vector<std::shared_ptr<HttpConnection>> connections;
    std::cout << "declare accept_handler" << std::endl;
    std::string data;
    std::function<void(const boost::system::error_code &,
                       std::shared_ptr<HttpConnection>)>
        accept_handler;
    auto router = std::shared_ptr<router_type>(new router_type());
    (*router)["^/(.*)$"]["GET"] = [&mime](std::shared_ptr<Request> req,
                                          std::shared_ptr<Response> res) {
        try {
            auto web_root_path = boost::filesystem::canonical("../static");
            auto path = boost::filesystem::canonical(web_root_path / req->url);
            // Check if path is within web_root_path

            if (boost::filesystem::is_directory(path)) path /= "index.html";

            if (boost::filesystem::exists(path)) res->send_file(path);

        } catch (const std::exception &ex) {
            std::cout << ex.what() << std::endl;
        }
    };
    (*router)["^/json$"]["POST"] = [](std::shared_ptr<Request> req,
                                      std::shared_ptr<Response> res) {
        std::cout << "/json POST" << std::endl;
        std::cout << req->header["Content-Type"] << std::endl;
        std::cout << req->content << std::endl;
        auto json_data = json::parse(req->content);

        std::string json_str = json_data.dump();
        res->status(200).header("Content-Type", "application/json");
        res->header("Content-Length", std::to_string(json_str.length()));

        res->send_header();
        res->content << json_str << "\r\n";
    };
    accept_handler = [&io_ctx, &connections, &tcp_acceptor, &accept_handler,
                      &router](const boost::system::error_code &ec,
                               std::shared_ptr<HttpConnection> conn) {
        // std::cout << ec.value() << std::endl;
        if (ec == boost::asio::error::operation_aborted) {
            std::cout << "fuck up" << std::endl;
            return;
        }
        if (!ec) {
            conn->deal();
        }
        std::unique_ptr<ip::tcp::socket> new_sock_ptr(
            new ip::tcp::socket(io_ctx));
        std::shared_ptr<HttpConnection> new_conn =
            std::make_shared<HttpConnection>(std::move(new_sock_ptr), router);
        tcp_acceptor.async_accept(
            *new_conn->_socket,
            std::bind(accept_handler, std::placeholders::_1, new_conn));
    };
    std::unique_ptr<ip::tcp::socket> tcp_socket(new ip::tcp::socket(io_ctx));
    std::shared_ptr<HttpConnection> new_conn =
        std::make_shared<HttpConnection>(std::move(tcp_socket), router);
    std::cout << "async.accept" << std::endl;
    tcp_acceptor.async_accept(
        *new_conn->_socket,
        std::bind(accept_handler, std::placeholders::_1, new_conn));

    std::cout << "io_ctx.run()" << std::endl;
    io_ctx.run();

    return 0;
}