text:
#include "moon/Graphics.h"
#include "moon/runtime.h"
#include "moon/Blackboard.h"
#include "moon/Context.h"
#include "moon/Image.h"
#include "moon/SceneNode.h"

#include <painting2/RenderColorCommon.h>

#include <boost/filesystem.hpp>

#include <vector>
#include <algorithm>

struct lua_State;

namespace
{

ur::Context* UR_CTX = nullptr;

#define INSTANCE() (moon::Blackboard::Instance()->GetContext()->GetModuleMgr().GetModule<moon::Graphics>())

void _trans_from_table(lua_State* L, int idx, sm::Matrix2D& mt)
{
	enum TransIDX
	{
		X = 0,
		Y,
		A,
		SX,
		SY,
		OX,
		OY,
		KX,
		KY,
		TRANS_IDX_MAX,
	};
	float t[TRANS_IDX_MAX];
	memset(t, 0, sizeof(t));
	t[SX] = t[SY] = 1.0f;

	int n = (int)moon::luax_objlen(L, idx);
	for (int i = 0; i < n; ++i) {
		lua_rawgeti(L, idx, i + 1);
		t[i] = moon::luax_tofloat(L, -1);
		lua_pop(L, 1);
	}

	//mt.SetTransformation(x, -y, -a, sx, sy, ox, -oy, kx, ky);
	mt.SetTransformation(t[X], t[Y], t[A], t[SX], t[SY], t[OX], t[OY], t[KX], t[KY]);
}

void _color_from_table(lua_State* L, int idx, pt0::Color& col)
{
	for (int i = 1; i <= 4; i++) {
		lua_rawgeti(L, idx, i);
	}

	col.r = (uint8_t)luaL_checknumber(L, -4);
	col.g = (uint8_t)luaL_checknumber(L, -3);
	col.b = (uint8_t)luaL_checknumber(L, -2);
	col.a = (uint8_t)luaL_optnumber(L, -1, 255);

	lua_pop(L, 4);
}

int w_line(lua_State* L)
{
	int args = lua_gettop(L);
	bool is_table = false;
	if (args == 1 && lua_istable(L, 1))
	{
		args = (int)moon::luax_objlen(L, 1);
		is_table = true;
	}

	if (args % 2 != 0) {
		return luaL_error(L, "Number of vertex components must be a multiple of two");
	} else if (args < 4) {
		return luaL_error(L, "Need at least two vertices to draw a line");
	}

	std::vector<float> coords;
	coords.reserve(args);
	if (is_table)
	{
		for (int i = 0; i < args; ++i)
		{
			lua_rawgeti(L, 1, i + 1);
			coords.push_back(moon::luax_tofloat(L, -1));
			lua_pop(L, 1);
		}
	}
	else
	{
		for (int i = 0; i < args; ++i) {
			coords.push_back(moon::luax_tofloat(L, i + 1));
		}
	}
	for (int i = 0; i < args; ++i) {
		coords.push_back(moon::luax_tofloat(L, i + 1));
	}

	// reverse y
	for (int i = 1; i < args; i += 2) {
		coords[i] = -coords[i];
	}

	INSTANCE()->Polyline(&coords[0], coords.size());

	return 0;
}

int w_rectangle(lua_State* L)
{
	moon::Graphics::DrawMode mode;
	const char *str = luaL_checkstring(L, 1);
	if (!moon::Graphics::GetConstant(str, mode))
		return luaL_error(L, "Invalid draw mode: %s", str);

	float x = (float)luaL_checknumber(L, 2);
	float y = (float)luaL_checknumber(L, 3);
	float w = (float)luaL_checknumber(L, 4);
	float h = (float)luaL_checknumber(L, 5);

	if (lua_isnoneornil(L, 6))
	{
		INSTANCE()->Rectangle(mode, x, y, w, h);
		return 0;
	}

	float rx = (float)luaL_optnumber(L, 6, 0.0);
	float ry = (float)luaL_optnumber(L, 7, rx);

	int points;
	if (lua_isnoneornil(L, 8))
		points = std::max(rx, ry) > 20.0 ? (int)(std::max(rx, ry) / 2) : 10;
	else
		points = (int)luaL_checknumber(L, 8);

	// todo
	INSTANCE()->Rectangle(mode, x, y, w, h/*, rx, ry, points*/);
	return 0;
}

int w_circle(lua_State* L)
{
	moon::Graphics::DrawMode mode;
	const char *str = luaL_checkstring(L, 1);
	if (!moon::Graphics::GetConstant(str, mode)) {
		return luaL_error(L, "Invalid draw mode: %s", str);
	}

	float x = (float)luaL_checknumber(L, 2);
	float y = (float)luaL_checknumber(L, 3);
	float radius = (float)luaL_checknumber(L, 4);
	int points;
	if (lua_isnoneornil(L, 5))
		points = radius > 10 ? (int)(radius) : 10;
	else
		points = (int)luaL_checknumber(L, 5);

	INSTANCE()->Circle(mode, x, y, radius, points);
	return 0;
}

int w_polygon(lua_State* L)
{
	int args = lua_gettop(L) - 1;

	moon::Graphics::DrawMode mode;
	const char *str = luaL_checkstring(L, 1);
	if (!moon::Graphics::GetConstant(str, mode)) {
		return luaL_error(L, "Invalid draw mode: %s", str);
	}

	bool is_table = false;
	float *coords;
	if (args == 1 && lua_istable(L, 2))
	{
		args = (int)moon::luax_objlen(L, 2);
		is_table = true;
	}

	if (args % 2 != 0) {
		return luaL_error(L, "Number of vertex components must be a multiple of two");
	} else if (args < 6) {
		return luaL_error(L, "Need at least three vertices to draw a polygon");
	}

	// fetch coords
	coords = new float[args + 2];
	if (is_table)
	{
		for (int i = 0; i < args; ++i)
		{
			lua_rawgeti(L, 2, i + 1);
			coords[i] = moon::luax_tofloat(L, -1);
			lua_pop(L, 1);
		}
	}
	else
	{
		for (int i = 0; i < args; ++i) {
			coords[i] = moon::luax_tofloat(L, i + 2);
		}
	}

	// make a closed loop
	coords[args]   = coords[0];
	coords[args+1] = coords[1];

	// reverse y
	for (int i = 1; i < args + 2; i += 2) {
		coords[i] = -coords[i];
	}

	INSTANCE()->Polygon(mode, coords, args+2);
	delete[] coords;

	return 0;
}

int w_set_color(lua_State* L)
{
	pt0::Color c;
	if (lua_istable(L, 1))
	{
		_color_from_table(L, 1, c);
	}
	else
	{
		c.r = (uint8_t)luaL_checknumber(L, 1);
		c.g = (uint8_t)luaL_checknumber(L, 2);
		c.b = (uint8_t)luaL_checknumber(L, 3);
		c.a = (uint8_t)luaL_optnumber(L, 4, 255);
	}
	INSTANCE()->SetColor(c);
	return 0;
}

int w_get_color(lua_State* L)
{
	auto& c = INSTANCE()->GetColor();
	lua_pushnumber(L, c.r);
	lua_pushnumber(L, c.g);
	lua_pushnumber(L, c.b);
	lua_pushnumber(L, c.a);
	return 4;
}

int w_set_background_color(lua_State* L)
{
	pt0::Color c;
	if (lua_istable(L, 1))
	{
		_color_from_table(L, 1, c);
	}
	else
	{
		c.r = (uint8_t)luaL_checknumber(L, 1);
		c.g = (uint8_t)luaL_checknumber(L, 2);
		c.b = (uint8_t)luaL_checknumber(L, 3);
		c.a = (uint8_t)luaL_optnumber(L, 4, 255);
	}
	INSTANCE()->SetBackgroundColor(c);
	return 0;
}

int w_set_line_width(lua_State* L)
{
	float width = (float)luaL_checknumber(L, 1);
	INSTANCE()->SetLineWidth(width);
	return 0;
}

int w_get_font(lua_State* L)
{
	return 0;
}

int w_set_font(lua_State* L)
{
	return 0;
}

int w_draw(lua_State* L)
{
	if (!moon::luax_istype(L, 1, moon::SCENE_NODE_ID) &&
		!moon::luax_istype(L, 1, moon::IMAGE_ID)) {
		return 0;
	}

	int args = lua_gettop(L);

	sm::Matrix2D mt;
	if (args > 1) {
		_trans_from_table(L, 2, mt);
	}

	if (moon::luax_istype(L, 1, moon::SCENE_NODE_ID))
	{
		auto node = moon::luax_checktype<moon::SceneNode>(L, 1, moon::SCENE_NODE_ID);
		moon::luax_catchexcept(L, [&]() {
//			node->Draw(mt);
		});
	}
	else if (moon::luax_istype(L, 1, moon::IMAGE_ID))
	{
		auto img = moon::luax_checktype<moon::Image>(L, 1, moon::IMAGE_ID);
		moon::luax_catchexcept(L, [&]() {
//			img->Draw(mt);
		});
	}

	return 0;
}

int w_print(lua_State* L)
{
	const char* str = luaL_checkstring(L, 1);

	int args = lua_gettop(L);

	sm::Matrix2D mt;
	if (args > 1) {
		_trans_from_table(L, 2, mt);
	}

	pt2::RenderColorCommon col;
	if (args > 2) {
		_color_from_table(L, 3, col.mul);
	}

	moon::luax_catchexcept(L, [&]() {
		INSTANCE()->Print(*UR_CTX, str, mt, col);
	});

	return 0;
}

int w_printf(lua_State* L)
{
	const char* str = luaL_checkstring(L, 1);

	int args = lua_gettop(L);

	sm::Matrix2D mt;
	if (args > 1) {
		_trans_from_table(L, 2, mt);
	}

	pt2::RenderColorCommon col;
	if (args > 2) {
		_color_from_table(L, 3, col.mul);
	}

	moon::luax_catchexcept(L, [&]() {
		INSTANCE()->Print(*UR_CTX, str, mt, col);
	});

	return 0;
}

int w_get_width(lua_State* L)
{
	int w;
	moon::luax_catchexcept(L, [&]() {
		w = INSTANCE()->GetWidth();
	});
	lua_pushinteger(L, w);
	return 1;
}

int w_get_height(lua_State* L)
{
	int h;
	moon::luax_catchexcept(L, [&]() {
		h = INSTANCE()->GetHeight();
	});
	lua_pushinteger(L, h);
	return 1;
}

int w_new_image(lua_State* L)
{
	//auto bb = moon::Blackboard::Instance();

	//const char* filepath = luaL_checkstring(L, 1);
	//auto real_path = boost::filesystem::absolute(filepath, bb->GetWorkDir());

	//moon::Image* img = nullptr;
	//moon::luax_catchexcept(L, [&]() { img = new moon::Image(real_path.string()); });
	//moon::luax_pushtype(L, moon::IMAGE_ID, img);
	//img->Release();

	//return 1;

    return 0;
}

}

namespace moon
{

// List of functions to wrap.
static const luaL_Reg functions[] =
{
	// shape
	{ "line", w_line },
	{ "rectangle", w_rectangle },
	{ "circle", w_circle },
	{ "polygon", w_polygon },

	// state
	{ "set_color", w_set_color },
	{ "get_color", w_get_color },
	{ "set_background_color", w_set_background_color },
	{ "set_line_width", w_set_line_width },
	{ "get_font", w_get_font },
	{ "set_font", w_set_font },

	{ "draw", w_draw },

	{ "print", w_print },
	{ "printf", w_printf },

	{ "get_width", w_get_width },
	{ "get_height", w_get_height },

	{ "new_image", w_new_image },

	{ 0, 0 }
};

// Types for this module.
static const lua_CFunction types[] =
{
//	luaopen_image,
	0
};

extern "C" int luaopen_moon_graphics(lua_State* L)
{
	Graphics* instance = INSTANCE();
	if (instance == nullptr) {
		luax_catchexcept(L, [&](){ instance = new Graphics(); });
	} else {
		instance->Retain();
	}

	WrappedModule w;
	w.module = instance;
	w.name = "graphics";
	w.type = MODULE_GRAPHICS_ID;
	w.functions = functions;
	w.types = types;

	return luax_register_module(L, w);
}

}

max_stars_repo_name: xzrunner/moon

max_issues_repo_name: xzrunner/moon

max_forks_repo_name: xzrunner/moon


text:
//
//  Copyright © 2015 Claus Christmann <hcc |ä| gatech.edu>.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

#include "vroniinterface.h"

/* VRONI header files
 *   These header files are part of the VRONI source code
 *   NOTE: The order of inlcusion does matter!
 */
#include <vroni.h>

#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/reverse.hpp>
#include <QtCore/QHash>
#include <QtCore/QDebug>

#include <unordered_map>

#include "Graphics/arrowitem.h"

VroniInterface::VroniInterface()
{
  // from VRONI docu:
  // call this routine once prior calling any other routine of VRONI
  VRONI::API_InitializeProgram();

  VRONI::quiet = true; // suppress printfs
}

VroniInterface::~VroniInterface()
{
  // from VRONI docu:
  // call this routine to release all memory allocated by VRONI
  VRONI::API_TerminateProgram();
}

void VroniInterface::resetAll(void )
{
  // call VRONI's API funtion
  VRONI::API_ResetAll();

  // mark the (now erased) VD/DT data as not current
  m_vdIsCurrent = false;
  m_isPointSite = true;
}

void VroniInterface::setVroniSiteData(const Geometry::Polygon_2D& complexPolygon)
{
  resetAll(); // reset VRONI's data...
  m_isPointSite = false;

  namespace bg = boost::geometry;

  // get the containing box of the polygon
  bg::model::box<Geometry::Point_2D> box;
  bg::envelope(complexPolygon,box);

  // set the scaling box from that
  auto x_min = box.min_corner().get<0>();
  auto y_min = box.min_corner().get<1>();
  auto x_max = box.max_corner().get<0>();
  auto y_max = box.max_corner().get<1>();

  m_scalingBox = ScalingBox(x_min,y_min,x_max,y_max); //NOTE: Warning, this could include an implicit conversion to double!

  // add the outer polygon
  addElementToSite( complexPolygon.outer());

  // add the inner holes, i.e. the obstacles
  auto obstacleList =  complexPolygon.inners();
  for(Geometry::Polygon_2D::ring_type const & ring : obstacleList)
  {
    addElementToSite(ring);
  }

  boundingBox();
}

void VroniInterface::setVroniSiteData(const Geometry::Polygon_2D& boundary, const std::unordered_map<ElementId, Obstacle >& obstacleData)
{
  resetAll(); // reset VRONI's data...
  m_isPointSite = false;

//   qDebug() << "ENTERING setVroniSiteData";
//   printVroniSiteData();

  namespace bg = boost::geometry;

  // get the containing box of the polygon
  bg::model::box<Geometry::Point_2D> box;
  bg::envelope(boundary,box);

  // set the scaling box from that
  auto x_min = box.min_corner().get<0>();
  auto y_min = box.min_corner().get<1>();
  auto x_max = box.max_corner().get<0>();
  auto y_max = box.max_corner().get<1>();

  m_scalingBox = ScalingBox(x_min,y_min,x_max,y_max); //NOTE: Warning, this could include an implicit conversion to double!

  // add the outer boundary to the site and add the shape identifier "0"
  int index = 0;

  Geometry::Point_2D centroid;
  boost::geometry::centroid(boundary.outer(),centroid);
  shapeCentroidMap.clear();
  shapeCentroidMap[index] = centroid;

  addElementToSite( boundary.outer(), index++);

  // add the obstacles to the site_file
  for( auto& pair : obstacleData  )
  {
    Obstacle const& obstacle = pair.second;

    //NOTE: we need to make a copy of the outer obstacle shape so that  we can
    // use bg::reverse. This is important so that the boundary and the obstacle
    // shapes have different orientations so that VRONI can correctly determined
    // "holes" and the "inside" of the arena.
    Geometry::Polygon_2D::ring_type tempRing;
    bg::assign(tempRing,obstacle.shape().outer());
    bg::reverse(tempRing);

    Geometry::Point_2D centroid;
    boost::geometry::centroid(tempRing,centroid);
    shapeCentroidMap[index] = centroid;

    addElementToSite( tempRing, index++ );
  }

  // update the bounding box
  boundingBox();

}

void VroniInterface::setVroniSiteData(const Geometry::MultiPoint_2D& mulitPoint)
{
  resetAll(); // reste VRONI's internal site data
  m_isPointSite = true;

  namespace bg = boost::geometry;

  // get the containing box of the point set
  boost::geometry::model::box<Geometry::Point_2D> box;
  boost::geometry::envelope(mulitPoint,box);

  // set the scaling box from that
  auto x_min = box.min_corner().get<0>();
  auto y_min = box.min_corner().get<1>();
  auto x_max = box.max_corner().get<0>();
  auto y_max = box.max_corner().get<1>();

  m_scalingBox = ScalingBox(x_min,y_min,x_max,y_max); //NOTE: Warning, this could include an implicit conversion to double!

  // add the points to the internal VRONI site data set
  addElementToSite(mulitPoint);

  boundingBox();

}

void VroniInterface::addElementToSite(const Geometry::Polygon_2D::ring_type& ring, VRONI::eas_type data)
{
  using namespace VRONI;

  m_isPointSite = false; //NOTE: this function adds non-points to the VRONI site

  // get the number of edges of the geometry
  std::size_t num_edges = boost::geometry::num_points(ring) - 1; // this assumes that the ring is closed!

  // preallocate a VRONI in_segs array to hold the edges' data
  in_segs* edges = new in_segs[num_edges];

  // populate the edges array
  int i=0;
  for( auto it_point=ring.begin() ; it_point !=--(ring.end()); /* no action */)
  {
    edges[i].x1 = boost::geometry::get<0>(*it_point); //NOTE: Warning: this could cause an implicit conversion
    edges[i].y1 = boost::geometry::get<1>(*it_point); //NOTE: Warning: this could cause an implicit conversion
    ++it_point;
    edges[i].x2 = boost::geometry::get<0>(*it_point); //NOTE: Warning: this could cause an implicit conversion
    edges[i].y2 = boost::geometry::get<1>(*it_point); //NOTE: Warning: this could cause an implicit conversion

    edges[i].ext_appl = data;
    ++i;
  }

  // store whether or not VRONI determined the API input to be new or not
  boolean new_input = false;

  // Call the VRONI API to add the edge segments representing the QPolygon
  API_ArrayInput(
    0,nullptr,   // not adding any points
    num_edges, edges,   // adding num_edges segments, stored in/at edges
    0,nullptr,     // not adding any arcs
    &new_input    // check if VRONI determines this is a new input
  );

  if( new_input )
  {
    // reset the Voronoi diagram tracker
    m_vdIsCurrent = false;
  }

  // free the allocated memory temporarily holding the edges.
  // VRONIs API_ArrayInput performs an internal copy-by-value, hence this is
  // safe
  delete[] edges;
}

void VroniInterface::addElementToSite(const Geometry::Point_2D& point)
{
  using namespace VRONI;

  // get/set the number of points to add
  int num_points = 1;

  // preallocate a VRONI in_pnts array to hold the points' data
  in_pnts * points = new in_pnts[num_points];

  // populate the points array
  int i = 0;
  {
    points[i].x1 =  boost::geometry::get<0>(point);
    points[i].y1 =  boost::geometry::get<1>(point);
  }

  // store whether or not VRONI determined the API input to be new or not
  boolean new_input = false;

  // Call the VRONI API to add the edge segments representing the QPolygon
  API_ArrayInput(
    num_points, points,   // adding num_points elements, stored in/at points
    0,nullptr,   // not adding any edges
    0,nullptr,     // not adding any arcs
    &new_input    // check if VRONI determines this is a new input
  );

  if( new_input )
  {
    // reset the Voronoi diagram tracker
    m_vdIsCurrent = false;
  }

  // free the allocated memory temporarily holding the edges.
  // VRONIs API_ArrayInput performs an internal copy-by-value, hence this is
  // safe
  delete[] points;

}

void VroniInterface::addElementToSite(const Geometry::MultiPoint_2D& mulitPoint)
{
  using namespace VRONI;

  // get/set the number of points to add
  int num_points = boost::geometry::num_geometries(mulitPoint);

  // preallocate a VRONI in_pnts array to hold the points' data
  in_pnts * points = new in_pnts[num_points];

  // populate the points array
  int i = 0;
  for( auto & point : mulitPoint )
  {
    points[i].x1 =  boost::geometry::get<0>(point);
    points[i].y1 =  boost::geometry::get<1>(point);
    ++i;
  }

  // store whether or not VRONI determined the API input to be new or not
  boolean new_input = false;

  // Call the VRONI API to add the edge segments representing the QPolygon
  API_ArrayInput(
    num_points, points,   // adding num_points elements, stored in/at points
    0,nullptr,   // not adding any edges
    0,nullptr,     // not adding any arcs
    &new_input    // check if VRONI determines this is a new input
  );

  if( new_input )
  {
    // reset the Voronoi diagram tracker
    m_vdIsCurrent = false;
  }

  // free the allocated memory temporarily holding the edges.
  // VRONIs API_ArrayInput performs an internal copy-by-value, hence this is
  // safe
  delete[] points;
}

#ifdef WITH_GRAPHICS

QGraphicsItemGroup* VroniInterface::getSiteGraphics()
{
  auto group = new QGraphicsItemGroup;

  QColor pntColor = Qt::red;    /// PNT site color
  QColor segColor = Qt::blue;   /// SEG site color
  QColor arcColor = Qt::green;  /// ARC site color

//   using LineGraphics = QGraphicsLineItem;
  using LineGraphics = Graphics_2D::Arrow;
  QPen segPen = QPen(segColor);

  using ArcGraphics = QGraphicsEllipseItem;
  QPen arcPen = QPen(arcColor);
  const bool approxArcsByLines = true;
  const bool drawArcCenter = true;

  using VertexGraphics = QGraphicsEllipseItem;
  QPen pntPen = QPen(pntColor);
  constexpr qreal radius = 4.0;

  // add segments
  for( int i = 0; i<VRONI::num_segs; ++i)
  {
    VRONI::seg* vr_seg  = VRONI::segs+i;

    Geometry::Point_2D tail;
    get_pnt_coords(tail,vr_seg->i1);

    Geometry::Point_2D head;
    get_pnt_coords(head,vr_seg->i2);

    auto line = new LineGraphics(tail.x(), tail.y(),
                                 head.x(), head.y());

    line->setPen(segPen);
    group->addToGroup(line);
  }

  // add arcs
  for(int i = 0; i<VRONI::num_arcs; ++i)
  {
    VRONI::arc* vr_arc = VRONI::arcs+i;

    if( approxArcsByLines )
    {
      Geometry::Point_2D tail;
      get_pnt_coords(tail,vr_arc->i1);

      Geometry::Point_2D head;
      get_pnt_coords(head,vr_arc->i2);

      if( boost::geometry::distance(tail,head) > ZERO )
      {
        auto line = new LineGraphics(tail.x(), tail.y(),
                                  head.x(), head.y());
        group->addToGroup(line);
      }
    }

    if( drawArcCenter )
    {
      Geometry::Point_2D center(vr_arc->c.x, vr_arc->c.y);
      if(VRONI::scale_data)
      { center = unScale(center); }

      auto point = new VertexGraphics(center.x()-radius/2, center.y()-radius/2,
                                      radius,radius);
    }

  }

  // add points
  for( int i = 0; i<VRONI::num_pnts; ++i )
  {
    Geometry::Point_2D point;
    get_pnt_coords(point, i);

    auto vertex = new VertexGraphics(point.x()-radius/2, point.y()-radius/2,
                                      radius,radius);
    vertex->setPen(pntPen);
    group->addToGroup(vertex);
  }

  printVroniSiteData();

  return group;
}

QGraphicsItemGroup* VroniInterface::getNodeGraphics()
{
  auto group = new QGraphicsItemGroup;

  QColor nodeColor = Qt::darkRed;

  int num_nodes = VRONI::GetNumberOfNodes();

  using VertexGraphics = QGraphicsEllipseItem;
  QPen nodePen = QPen(nodeColor);
  constexpr qreal radius = 4.0;

  // add nodes
  for( int i = 0; i<num_nodes; ++i )
  {
    Geometry::Point_2D node;
    get_node_coords(node,i);

    auto vertex = new VertexGraphics(node.x()-radius/2, node.y()-radius/2,
                                      radius,radius);
    vertex->setPen(nodePen);
    group->addToGroup(vertex);

  }

  return group;
}

QGraphicsItemGroup* VroniInterface::getEdgeGraphics()
{
  auto group = new QGraphicsItemGroup;

  QColor edgeColor = Qt::darkGreen;

  int num_edges = VRONI::GetNumberOfEdges();

  using LineGraphics = Graphics_2D::Arrow;
//   using LineGraphics = QGraphicsLineItem;
  QPen edgePen = QPen(edgeColor);
  edgePen.setWidthF(0);

  // add edges
  for( int i=0; i< num_edges; ++i)
  {
    VRONI::edge* vr_edge  = VRONI::edges+i;

    Geometry::Point_2D tail;
    get_node_coords(tail,vr_edge->n1);

    Geometry::Point_2D head;
    get_node_coords(head,vr_edge->n2);

    if( boost::geometry::distance(tail,head) > ZERO ) // NOTE: ZERO is a VRONI #define
    {
      auto line = new LineGraphics(tail.x(), tail.y(),
                                  head.x(), head.y());

      line->setPen(edgePen);
      group->addToGroup(line);
    }
  }

  return group;
}

#endif // WITH_GRAPHICS

Simple2dGraph::Graph VroniInterface::createSiteShapeAssociationGraph()
{
  using Graph = Simple2dGraph::Graph;
  Graph graph;

  if( !m_vdIsCurrent )
  {
    qDebug() << "Thr Voronoi diagram is not current. Can't create the site-to-shape association graph.";
    return graph;
  }

  auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map

  QHash<int,Graph::vertex_descriptor> shapeVertexHash;

  // create the vertices representing the shapes (as they won't be itner connected)
  for( int i=0; i<shapeCentroidMap.size(); ++i)
  {
    Graph::vertex_descriptor shapeVertex = boost::add_vertex(graph);
        shapeVertexHash[i]=shapeVertex;

    vPosMap[shapeVertex] = shapeCentroidMap[i];
  }

  QHash<int,Graph::vertex_descriptor> segSiteHash;

  // create the vertices representing the SEG sites
  for( int i=0; i<VRONI::num_segs; ++i)
  {
    Graph::vertex_descriptor segVertex = boost::add_vertex(graph);
    segSiteHash[i]=segVertex;

    VRONI::seg* vr_seg = VRONI::segs+i;

    Geometry::Point_2D tail;
    get_pnt_coords(tail,vr_seg->i1);

    Geometry::Point_2D head;
    get_pnt_coords(head,vr_seg->i2);

    vPosMap[segVertex] = Geometry::midpoint(head,tail);

    boost::add_edge(segVertex,shapeVertexHash[vr_seg->ext_appl],graph);

  }

  // get the PNT-to-shape map
  QHash<int,int> pntShapeMap  = getPntToShapeMap();

  QHash<int,Graph::vertex_descriptor> pntSiteHash;

  // create the vertices representing the PNT sites
  for( int i=0; i<VRONI::num_pnts;++i )
  {
    Graph::vertex_descriptor pntVertex = boost::add_vertex(graph);
    pntSiteHash[i]=pntVertex;

    Geometry::Point_2D point;
    get_pnt_coords(point,i);

    vPosMap[pntVertex] = point;

    boost::add_edge(pntVertex,shapeVertexHash[pntShapeMap[i]],graph);
  }

  return graph;

}

Simple2dGraph::Graph VroniInterface::createEdgeSiteAssociationGraph()
{
  using Graph = Simple2dGraph::Graph;
  Graph graph;

  auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map

  QHash<int,Graph::vertex_descriptor> pntSiteHash;
  QHash<int,Graph::vertex_descriptor> segSiteHash;
  QHash<int,Graph::vertex_descriptor> arcSiteHash;
  QHash<int,Graph::vertex_descriptor> vdEdgeHash;

  // first, create the vertices representing the sites (as they won't be connected)

  for( int i=0; i<VRONI::num_pnts;++i )
  {
    Graph::vertex_descriptor vertex = boost::add_vertex(graph);
    pntSiteHash[i]=vertex;

    Geometry::Point_2D point;
    get_pnt_coords(point,i);

    vPosMap[vertex] = point;
  }

  for( int i=0; i<VRONI::num_segs; ++i)
  {
    Graph::vertex_descriptor vertex = boost::add_vertex(graph);
    segSiteHash[i]=vertex;

    VRONI::seg* vr_seg = VRONI::segs+i;

    Geometry::Point_2D tail;
    get_pnt_coords(tail,vr_seg->i1);

    Geometry::Point_2D head;
    get_pnt_coords(head,vr_seg->i2);

    vPosMap[vertex] = Geometry::midpoint(head,tail);
  }

  for( int i=0; i<VRONI::num_arcs; ++i)
  {
    Graph::vertex_descriptor vertex = boost::add_vertex(graph);
    arcSiteHash[i]=vertex;

    VRONI::arc* vr_arc = VRONI::arcs+i;

    Geometry::Point_2D tail;
    get_pnt_coords(tail,vr_arc->i1);

    Geometry::Point_2D head;
    get_pnt_coords(head,vr_arc->i2);

    vPosMap[vertex] = Geometry::midpoint(head,tail);
  }

  auto inclusion_check = always_true;

  // second, create the vertices representing the edges and connect the resp.
  // vertices
  for( int i=0; i<VRONI::GetNumberOfEdges(); ++i)
  {
    VRONI::edge* vr_edge = VRONI::edges+i;

    if( inclusion_check(vr_edge))
    {
      Graph::vertex_descriptor vertex = boost::add_vertex(graph);
      vdEdgeHash[i] = vertex;

      Geometry::Point_2D tail;
      get_node_coords(tail,vr_edge->n1);

      Geometry::Point_2D head;
      get_node_coords(head,vr_edge->n2);

      vPosMap[vertex] = Geometry::midpoint(head,tail);

      // get left site vertex

      Graph::vertex_descriptor left;
      switch(vr_edge->ltype)
      {
        case VRONI::PNT : left = pntSiteHash[vr_edge->lft]; break;
        case VRONI::SEG : left = segSiteHash[vr_edge->lft]; break;
        case VRONI::ARC : left = arcSiteHash[vr_edge->lft]; break;
        default: ;  // this shouldn't happen
      }
      boost::add_edge(left,vertex,graph);

      Graph::vertex_descriptor right;
      switch(vr_edge->rtype)
      {
        case VRONI::PNT : right = pntSiteHash[vr_edge->rgt]; break;
        case VRONI::SEG : right = segSiteHash[vr_edge->rgt]; break;
        case VRONI::ARC : right = arcSiteHash[vr_edge->rgt]; break;
        default: ;  // this shouldn't happen
      }
      boost::add_edge(right,vertex,graph);
    }

  }

  return graph;

}

Simple2dGraph::Graph VroniInterface::createEdgeShapeAssociationGraph()
{
  using Graph = Simple2dGraph::Graph;
  Graph graph;

  if( !m_vdIsCurrent )
  {
    qDebug() << "Thr Voronoi diagram is not current. Can't create the edge-to-shape association graph.";
    return graph;
  }

  auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map

  QHash<int,Graph::vertex_descriptor> shapeHash;
  QHash<int,Graph::vertex_descriptor> vdEdgeHash;

  // create the vertices representing the shapes (as they won't be connected)
  for( int i=0; i<shapeCentroidMap.size(); ++i)
  {
    Graph::vertex_descriptor shapeVertex = boost::add_vertex(graph);
    shapeHash[i]=shapeVertex;

    vPosMap[shapeVertex] = shapeCentroidMap[i];
  }

  // get the SITE-to-shape maps
  QHash<int,int> pntToShapeMap  = getPntToShapeMap();
  QHash<int,int> segToShapeMap  = getSegToShapeMap();

  printVroniSiteData();
  qDebug() << "PNT-to-Shape Map:\n" <<  pntToShapeMap;
  qDebug() << "SEG-to-Shape Map:\n" <<  segToShapeMap;

  auto inclusion_check = in_w_mat;

  // third, create the vertices representing the edges and connect the resp.
  // vertices
  for( int i_vr_edge=0; i_vr_edge<VRONI::GetNumberOfEdges(); ++i_vr_edge)
  {
    VRONI::edge* vr_edge = VRONI::edges+i_vr_edge;

    // check whether the current edge should be included in this extraction
    if( inclusion_check(vr_edge ) )
    {
//       QString edgeString = QString("Edge %1 :").arg(i_vr_edge,2);

      Graph::vertex_descriptor edgeVertex = boost::add_vertex(graph);
      vdEdgeHash[i_vr_edge] = edgeVertex;

      Geometry::Point_2D tail;
      get_node_coords(tail,vr_edge->n1);

      Geometry::Point_2D head;
      get_node_coords(head,vr_edge->n2);

      vPosMap[edgeVertex] = Geometry::midpoint(head,tail);

      // get left site index

      int leftShapeIndex = NIL;
      switch(vr_edge->ltype)
      {
        case VRONI::PNT :
          // NOTE: the .contains check is necessary as operator[] default
          // constructs an entry in case the key isn't present -- and that default
          // constucted int is not VRONI::NIL.
          if( pntToShapeMap.contains(vr_edge->lft) )
          { leftShapeIndex = pntToShapeMap[vr_edge->lft]; }
//           edgeString.append(QString("PNT %1").arg(vr_edge->lft,2));
          break;
        case VRONI::SEG :
          if( segToShapeMap.contains(vr_edge->lft) )
          { leftShapeIndex = segToShapeMap[vr_edge->lft]; }
//           edgeString.append(QString("SEG %1").arg(vr_edge->lft,2));
          break;
        default: ; // this shouldn' happen
      }

      if( leftShapeIndex != NIL )
      {
        Graph::vertex_descriptor leftShape = shapeHash[leftShapeIndex];
        if( leftShapeIndex!=0 ) // don't add edges to the boundary
        { boost::add_edge(leftShape,edgeVertex,graph); }
//         edgeString.append(QString(" (# %1)").arg(leftShapeIndex,2));
      }
//       else
//       { edgeString.append(QString("       ")); } // space for the alignment

      int rightShapeIndex = NIL;
      switch(vr_edge->rtype)
      {
        case VRONI::PNT :
          // NOTE: the .contains check is necessary as operator[] default
          // constructs an entry in case the key isn't present -- and that default
          // constucted int is not VRONI::NIL.
          if( pntToShapeMap.contains(vr_edge->rgt) )
          { rightShapeIndex = pntToShapeMap[vr_edge->rgt]; }
//           edgeString.append(QString("PNT %1").arg(vr_edge->rgt,2));
          break;
        case VRONI::SEG :
          if( segToShapeMap.contains(vr_edge->rgt) )
          { rightShapeIndex = segToShapeMap[vr_edge->rgt]; }
//           edgeString.append(QString("SEG %1").arg(vr_edge->rgt,2));
          break;
        default: ; // this shouldn' happen
      }

      if( rightShapeIndex != NIL )
      {
        Graph::vertex_descriptor rightShape = shapeHash[rightShapeIndex];
        if( rightShapeIndex != 0 ) // don't add edges to the boundary
        { boost::add_edge(rightShape,edgeVertex,graph); }
//         edgeString.append(QString(" (# %1)").arg(rightShapeIndex,2));
      }
//       else
//       { edgeString.append(QString("       ")); } // space for the alignment

//       qDebug() << edgeString;
    }
  }

  return graph;
}

Simple2dGraph::Graph VroniInterface::createEdgeShapeAssociationGraph(
  QHash<int,int> shapeToEdgeMap
)
{
  using Graph = Simple2dGraph::Graph;
  Graph graph;

//   auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map

  QHash<int,Graph::vertex_descriptor> vdEdgeHash;
  auto inclusion_check = in_w_mat;

  // create the vertices representing the edges
  for( int i_vr_edge=0; i_vr_edge<VRONI::GetNumberOfEdges(); ++i_vr_edge)
  {
    VRONI::edge* vr_edge = VRONI::edges+i_vr_edge;

    // check whether the current edge should be included in this extraction
    if( inclusion_check(vr_edge ) )
    {
      Graph::vertex_descriptor edgeVertex = boost::add_vertex(graph);
      vdEdgeHash[i_vr_edge] = edgeVertex;

      Geometry::Point_2D tail;
      get_node_coords(tail,vr_edge->n1);

      Geometry::Point_2D head;
      get_node_coords(head,vr_edge->n2);

      vPosMap[edgeVertex] = Geometry::midpoint(head,tail);
    }
  }

  QHash<int,Graph::vertex_descriptor> shapeHash;

  // create the vertices representing the shapes and connect them to the edges
  for( int i=0; i<shapeCentroidMap.size(); ++i)
  {

    // create vertex

    Graph::vertex_descriptor shapeVertex = boost::add_vertex(graph);
    shapeHash[i]=shapeVertex;

    vPosMap[shapeVertex] = shapeCentroidMap[i];

    // connect to edge(s)

    auto connectedEdges = shapeToEdgeMap.values(i);

    for( auto edge : connectedEdges)
    {
      if( vdEdgeHash.contains(edge) )
      { boost::add_edge(shapeVertex,vdEdgeHash[edge],graph); }
    }

  }

  return graph;
}

void VroniInterface::computeVD(const VroniInterface::VDDTParameter& params)
{
  // VRONI is not const correct, hence some const casting is necessary
  auto output_file = const_cast<char*>( params.site_file.c_str() );
  auto vd_dt_file  = const_cast<char*>( params.points_VDDT_file.c_str() );

  // call the VRONI function computing the Voronoi diagram and the Delaunay
  // triangulation
  VRONI::API_ComputeVD(
    params.save_input, params.new_data, params.timing, params.bound,
    params.sample,params.approx, output_file, params.check_for_duplicates,
    params.recover_arcs, params.apx_absolute, params.apx_relative,
    params.auto_apx, params.points_only, params.save_point_output, vd_dt_file,
    params.clean_data
  );

  // mark the current VD/DT data as current
  m_vdIsCurrent = true;
}

/** \todo Maybe introduce an exception to indicate the out-of-dateness of the
 *    VD/DT data instead of implicitely calling computeVD()? This could
 *    improve the const-correctness of the overall interface.
 */
void VroniInterface::computeWMAT(double WMAT_distance, double WMAT_angle, bool auto_WMAT, bool left, bool right, bool timing)
{
  // check if the VD/DT data is current. If not, update the VD/DT data
  if( !m_vdIsCurrent )
  { computeVD(); }

  // call VRONI's API function
  // NOTE: technically the function prototype calls for VRONI:boolean as bool
  //   input types. But since those are just typdef'ed to bool this should work.
  VRONI::API_ComputeWMAT(auto_WMAT, WMAT_angle,WMAT_distance,timing,left,right);
}

void VroniInterface::computeWMAT(const VroniInterface::WMATParameter& params)
{
  // check if the VD/DT data is current. If not, update the VD/DT data
  if( !m_vdIsCurrent )
  { computeVD(); }

  // call VRONI's API function
  // NOTE: technically the function prototype calls for VRONI:boolean as bool
  //   input types. But since those are just typdef'ed to bool this should work.
  VRONI::API_ComputeWMAT(
    params.auto_WMAT, params.WMAT_angle, params.WMAT_distance, params.timing,
    params.left, params.right
  );
}

VroniGraph::Graph VroniInterface::extractVdGraph(std::function< bool (VRONI::edge*)> inclusion_check) const
{
  using Graph = VroniGraph::Graph;
  Graph graph; ///< a new boost graph to hold the VD graph.

  auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map
  auto vVroniMap  = boost::get(boost::vertex_vroniIndex, graph); ///< Vertex_VroniIndex_Map
  auto vDataMap   = boost::get(boost::vertex_vroniNodeData, graph); ///<  Vertex_VroniNodeData_Map
  auto eWeightMap = boost::get(boost::edge_weight, graph); ///<  Edge_Weight_Map
  auto eVroniMap  = boost::get(boost::edge_vroniIndex, graph); ///<  Edge_VroniIndex_Map
  auto eDataMap   = boost::get(boost::edge_vroniEdgeData, graph); ///<  Edge_VroniEdgeData_Map

  // A hash to relate VRONI node indices to the corresponding boost vertices
  QHash<int,Graph::vertex_descriptor> vroniIndexVertexHash;
  // A hash to relate VRONI edge indices to the corresponding boost edges
  QHash<int,Graph::edge_descriptor>   vroniIndexEdgeHash;

  // A counter to assign consecutive indices to the vertices (which will
  // differ from the VRONI indices stored in vVroniMap!)
  unsigned int vertexCounter = 0;

  auto get_vertex_descriptor = [&](Graph::vertex_descriptor & vertex, int vroniNodeIndex)
  {
    if( !vroniIndexVertexHash.contains(vroniNodeIndex) )
    { //no. add a new vertex.
      vertex = boost::add_vertex(graph);
      vroniIndexVertexHash[vroniNodeIndex]=vertex;

      { // populate the vertex properties

        vIndexMap[vertex]=vertexCounter++;
        vVroniMap[vertex]=vroniNodeIndex;

        VRONI::node* vroniNode = VRONI::nodes+vroniNodeIndex;
        vDataMap[vertex]=*vroniNode;

        Geometry::Point_2D position(vroniNode->p.x,vroniNode->p.y);
        if( VRONI::scale_data)
        { position = unScale(position); }
        vPosMap[vertex]=position;

      }
    }
    else
    { //yes. recover it.
      vertex = vroniIndexVertexHash.value(vroniNodeIndex);
    }
  };

  //
  // process the VRONI data and copy them over to the boost graph
  // ============================================================
  //

  // The total number of edges in VRONI's internal data. This includes VD/DT and
  // WMAT edges, but no data from the input site.
  int number_of_edges = VRONI::GetNumberOfEdges();

  // the index for the currently processed VRONI edge
  for(  int i_vr_edge = 4; // NOTE: I have NO IDEA why it has to start on 4, but this is a straight copy from VRONI::AddWMATToBuffer().
        i_vr_edge < number_of_edges;
        ++i_vr_edge)
  { // loop over all edges in the VRONI data, i.e. WMAT and VD/DT:

    // get a pointer to the currently processed edge
    VRONI::edge* vr_edge = VRONI::edges+i_vr_edge;

    // check whether the current edge should be included in this extraction
    if( inclusion_check(vr_edge ) )
    {
      Graph::vertex_descriptor tail;
            get_vertex_descriptor( tail, vr_edge->n1 );

      Graph::vertex_descriptor head;
            get_vertex_descriptor( head, vr_edge->n2 );

      {/* connect the head and the tail in the boost graph and compute the edge
        * length
        */
        using namespace boost;

        bool success= false;
        Graph::edge_descriptor edge;
        boost::tie(edge,success) = boost::add_edge(tail, head, graph);

        if(success)
        {
          eVroniMap[edge]   = i_vr_edge;
          eDataMap[edge]    = *vr_edge;
          eWeightMap[edge]  = geometry::distance(vPosMap[head],vPosMap[tail]);
          vroniIndexEdgeHash[i_vr_edge] = edge;
        }
      }

    }
  }

  // Initialize the interior edge index
  auto eIndexMap  = boost::get(boost::edge_index, graph); ///<  Edge_Index_Map
  boost::graph_traits<Graph>::edges_size_type edge_count = 0;
  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
  for(boost::tie(ei, ei_end) = boost::edges(graph); ei != ei_end; ++ei)
  { eIndexMap[*ei] = edge_count++; }

  // done
  return graph;
}

VroniGraph::Graph VroniInterface::extractDtGraph(void ) const
{
  using Graph = VroniGraph::Graph;

  if( !m_isPointSite )
  {
    qDebug() << "The VRONI site isn't a pure point site. Cannot extract Delaunay triangulation and/or graph.";
    return Graph();
  }

  Graph graph; ///< a new boost graph to hold the DT graph.

  auto vIndexMap  = boost::get(boost::vertex_index,graph); ///< Vertex_Index_Map
  auto vPosMap    = boost::get(boost::vertex_position_2D, graph); ///< Vertex_Position2d_Map
  auto vVroniMap  = boost::get(boost::vertex_vroniIndex, graph); ///< Vertex_VroniIndex_Map
  auto vDataMap   = boost::get(boost::vertex_vroniNodeData, graph); ///<  Vertex_VroniNodeData_Map
  auto eWeightMap = boost::get(boost::edge_weight, graph); ///<  Edge_Weight_Map

  //NOTE: There is no VRONI data associated with these edges, as VRONI doesn't
  // explicitely compute them.
  //   auto eVroniMap  = boost::get(boost::edge_vroniIndex, graph); ///<  Edge_VroniIndex_Map
  //   auto eDataMap   = boost::get(boost::edge_vroniEdgeData, graph); ///<  Edge_VroniEdgeData_Map

  // A hash to relate VRONI node indices to the corresponding boost vertices
  QHash<int,Graph::vertex_descriptor> vroniIndexHash;

  // A counter to assigne consecutive indices to the vertices (which will
  // differ from the VRONI indices stored in vVroniMap!)
  unsigned int vertexCounter = 0;

  // lambda to check if any of those VRONI nodes already is in the index hash
  auto get_site_vertex = [&](Graph::vertex_descriptor & vertex, int vroniNodeIndex)
  {
    if( !vroniIndexHash.contains(vroniNodeIndex) )
    { //no. add a new vertex.
      vertex = boost::add_vertex(graph);
      vroniIndexHash[vroniNodeIndex]=vertex;

      { // populate the vertex properties

        vIndexMap[vertex]=vertexCounter++;
        vVroniMap[vertex]=vroniNodeIndex;

        VRONI::node* vroniNode = VRONI::nodes+vroniNodeIndex;
        vDataMap[vertex]=*vroniNode;

        Geometry::Point_2D position(VRONI::pnts[vroniNodeIndex+2].p.x/*NOTE: this is different from vroniNode->p.x*/,
                                    VRONI::pnts[vroniNodeIndex+2].p.y/*NOTE: this is different from vroniNode->p.y*/);
        if( VRONI::scale_data)
        { position = unScale(position); }
        vPosMap[vertex]=position;

      }
    }
    else
    { //yes. recover it.
      vertex = vroniIndexHash.value(vroniNodeIndex);
    }
  };

  // get the Delaunay triangulation from VRONI
  std::vector<VRONI::d_triangle> dtTriangles = VRONI::GetDelaunayTriangles();

  for( VRONI::d_triangle& triangle : dtTriangles )
  {
    // get the involved nodes
    Graph::vertex_descriptor n1;
        get_site_vertex(n1,triangle.n1);
    Graph::vertex_descriptor n2;
        get_site_vertex(n2,triangle.n2);
    Graph::vertex_descriptor n3;
        get_site_vertex(n3,triangle.n3);

    // connect them in the boost graph and compute the edge lengths
    {

      bool success= false;
      Graph::edge_descriptor edge;

      boost::tie(edge,success) = boost::add_edge(n1, n2, graph);
      if(success)
      {

        eWeightMap[edge] = boost::geometry::distance(vPosMap[n1],vPosMap[n2]);
      }

      boost::tie(edge,success) = boost::add_edge(n2, n3, graph);
      if(success)
      {

        eWeightMap[edge] = boost::geometry::distance(vPosMap[n2],vPosMap[n3]);
      }

      boost::tie(edge,success) = boost::add_edge(n3, n1, graph);
      if(success)
      {

        eWeightMap[edge] = boost::geometry::distance(vPosMap[n3],vPosMap[n1]);
      }

    }

  }

//   VRONI::WriteVDDT("vddt.out");

  return graph;

}

VroniGraph::Graph VroniInterface::extractVdGraph(void ) const
{
  return extractVdGraph(always_true);
}
VroniGraph::Graph VroniInterface::extractWmatGraph(void ) const
{
  return extractVdGraph(in_w_mat);
}
VroniGraph::Graph VroniInterface::extractNonWmatGraph(void ) const
{
  return extractVdGraph(in_vddt);
}

QHash< int, int > VroniInterface::determineVoronoiCells()
{
  QHash<int,int> shapeToEdgeMap;

  if( !m_vdIsCurrent )
  {
    qDebug() << "Thr Voronoi diagram is not current. Can't determine the Voronoi cells.";
    return shapeToEdgeMap;
  }

  // get the SITE-to-shape maps
  QHash<int,int> pntToShapeMap  = getPntToShapeMap();
  QHash<int,int> segToShapeMap  = getSegToShapeMap();

  auto inclusion_check = in_w_mat;

  // third, create the vertices representing the edges and connect the resp.
  // vertices
  for( int i_vr_edge=0; i_vr_edge<VRONI::GetNumberOfEdges(); ++i_vr_edge)
  {
    VRONI::edge* vr_edge = VRONI::edges+i_vr_edge;

    // check whether the current edge should be included in this extraction
    if( inclusion_check(vr_edge ) )
    {

      int leftShapeIndex = NIL;
      switch(vr_edge->ltype)
      {
        case VRONI::PNT :
          // NOTE: the .contains check is necessary as operator[] default
          // constructs an entry in case the key isn't present -- and that default
          // constucted int is not VRONI::NIL.
          if( pntToShapeMap.contains(vr_edge->lft) )
          { leftShapeIndex = pntToShapeMap[vr_edge->lft]; }
          break;
        case VRONI::SEG :
          if( segToShapeMap.contains(vr_edge->lft) )
          { leftShapeIndex = segToShapeMap[vr_edge->lft]; }
          break;
        default: ; // this shouldn' happen
      }

      if( leftShapeIndex != NIL )
      { shapeToEdgeMap.insertMulti(leftShapeIndex,i_vr_edge); }

      int rightShapeIndex = NIL;
      switch(vr_edge->rtype)
      {
        case VRONI::PNT :
          // NOTE: the .contains check is necessary as operator[] default
          // constructs an entry in case the key isn't present -- and that default
          // constucted int is not VRONI::NIL.
          if( pntToShapeMap.contains(vr_edge->rgt) )
          { rightShapeIndex = pntToShapeMap[vr_edge->rgt]; }
          break;
        case VRONI::SEG :
          if( segToShapeMap.contains(vr_edge->rgt) )
          { rightShapeIndex = segToShapeMap[vr_edge->rgt]; }
          break;
        default: ; // this shouldn' happen
      }

      if( rightShapeIndex != NIL and rightShapeIndex != 0)
      { shapeToEdgeMap.insertMulti(rightShapeIndex,i_vr_edge); }

    }
  }

  return shapeToEdgeMap;
}

// QHash< int, Geometry::Polygon_2D > VroniInterface::extractVoronoiCellGeometries() const
// {
//   QHash<int, Geometry::Polygon_2D> geometries;
//
//   if( !m_vdIsCurrent )
//   {
//     qDebug() << "The VD is not current, cannot extract Voronoi cell geometries.";
//     return geometries;
//   }
//
//   QHash<int,int>  shapeToEdgeMap = determineVoronoiCells;
//   QList<int>      shapeIndices = shapeToEdgeMap.uniqueKeys;
//
//   for( auto shape_i : shapeIndices)
//   {
//     Geometry::MultiPoint_2D cellCorners;
//
//     // get a list of all edges associacted with that shape.
//     QList<int> edgeIndices = shapeToEdgeMap.values(shape_i);
//
//     for( auto edge_i : edgeIndices)
//     {
//       VRONI::edge* vr_edge = VRONI::edges+edge_i;
//
//       Geometry::Point_2D tail;
//       get_node_coords(tail,vr_edge->n1);
//       boost::geometry::append(cellCorners,tail);
//
//       Geometry::Point_2D head;
//       get_node_coords(tail,vr_edge->n2);
//       boost::geometry::append(cellCorners,head);
//     }
//
//     Geometry::Polygon_2D cellGeometry;
//     boost::geometry::convex_hull(cellCorners,cellGeometry);
//     geometries[shape_i] = cellGeometry;
//   }
//
//   return geometries;
// }

Geometry::Box_2D VroniInterface::boundingBox() const
{
  using namespace VRONI;

  if (!bbox_added) //NOTE: the bbox gets added in VRONI::AddDummyCorners(int bound)
    SetBoundingBox();

  if( data_scaled )
  {
    Geometry::Point_2D minCorner((bb_min.x/scale_factor)+shift.x,
                                 (bb_min.y/scale_factor)+shift.y);
    Geometry::Point_2D maxCorner((bb_max.x/scale_factor)+shift.x,
                                 (bb_max.y/scale_factor)+shift.y);

    return Geometry::Box_2D(minCorner,maxCorner);
  }
  else
  {
    Geometry::Point_2D minCorner((bb_min.x),
                                 (bb_min.y));
    Geometry::Point_2D maxCorner((bb_max.x),
                                 (bb_max.y));

    return Geometry::Box_2D(minCorner,maxCorner);

  }
}

Geometry::Point_2D VroniInterface::unScale(const Geometry::Point_2D& point) const
{
  if( VRONI::data_scaled )
    return Geometry::Point_2D(point.get<0>()/VRONI::scale_factor+VRONI::shift.x,
                              point.get<1>()/VRONI::scale_factor+VRONI::shift.y);
  else
    return point;
}

void VroniInterface::get_pnt_coords(Geometry::Point_2D& point, const int& pntIndex) const
{
  VRONI::pnt* vr_pnt = VRONI::pnts+pntIndex;
  point.set_x(vr_pnt->p.x);
  point.set_y(vr_pnt->p.y);
  if(VRONI::scale_data)
  { point = unScale(point); }
};

void VroniInterface::get_node_coords(Geometry::Point_2D& point, const int& nodeIndex) const
{
  VRONI::node* vr_node = VRONI::nodes+nodeIndex;
  point.set_x(vr_node->p.x);
  point.set_y(vr_node->p.y);
  if(VRONI::scale_data)
  { point = unScale(point); }
}

// void VroniInterface::unScale(Geometry::Point_2D& point) const
// {
//   point.get<0>() /= VRONI::scale_factor;
//   point.get<0>() += VRONI::shift.x;
//   point.get<1>() /= VRONI::scale_factor;
//   point.get<1>() += VRONI::shift.y;
// }

void VroniInterface::printVroniSiteData()
{
  qDebug() <<"VRONI Site Data:"
    <<"\nNumber of PNTs :" << VRONI::num_pnts
    <<"\nNumber of SEGs :" << VRONI::num_segs
    <<"\nNumber or ARCs :" << VRONI::num_arcs;

  qDebug() << "VRONI PNT Data :";
  for( int i=0; i<VRONI::num_pnts; ++i)
  {
    Geometry::Point_2D p;
    get_pnt_coords(p,i);
    qDebug() << QString("PNT %1:(%2,%3)").arg(i,2).arg(p.x()).arg(p.y());
  }

  qDebug() << "VRONI SEG Data :";
  for( int i=0; i<VRONI::num_segs; ++i)
  {

    Geometry::Point_2D tail,head;
    VRONI::seg* vr_seg = VRONI::segs+i;
    get_pnt_coords(tail,vr_seg->i1);
    get_pnt_coords(head,vr_seg->i2);

//     qDebug() << QString("SEG %1: PNT %2 -> PNT %3 : (%4,%5) -> (%6,%7)")
//     .arg(i,2)
//     .arg(vr_seg->i1,2)
//     .arg(vr_seg->i2,2)
//     .arg(tail.x(),8,'f',2).arg(tail.y(),8,'f',2)
//     .arg(head.x(),8,'f',2).arg(head.y(),8,'f',2) ;
    qDebug() << QString("SEG %1 (%2): PNT %3 -> PNT %4 ")
    .arg(i,2)
    .arg(vr_seg->ext_appl,2)
    .arg(vr_seg->i1,2)
    .arg(vr_seg->i2,2);
  }

}

QHash< int, int > VroniInterface::getSegToShapeMap() const
{
  QHash<int,int> segToShapeMap;

  for(int i=0; i<VRONI::num_segs; ++i)
  {
    VRONI::seg* vr_seg = VRONI::segs+i;
    segToShapeMap[i] = vr_seg->ext_appl;
  }

  return segToShapeMap;
}

QHash< int, int > VroniInterface::getPntToShapeMap() const
{
  QHash<int,int> pntShapeMap;

  // loop over all segments, grab the end points and the assoc. shape and
  // put it in the pntShapeMap
  for( int i=0; i< VRONI::num_segs; ++i)
  {
    VRONI::seg* vr_seg = VRONI::segs+i;

    pntShapeMap[vr_seg->i1] = vr_seg->ext_appl; // tail
    pntShapeMap[vr_seg->i2] = vr_seg->ext_appl; // head

  }

  return pntShapeMap;
}

bool VroniInterface::isVdCurrent() const
{
  return m_vdIsCurrent;
}

ext: cpp

max_stars_repo_name: mvsframework/mvs

max_issues_repo_name: mvsframework/mvs

max_forks_repo_name: mvsframework/mvs


text:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Main

#include "lt/slipstream/framing.h"

#include <boost/test/unit_test.hpp>
#include <rapidcheck/boost_test.h>

using namespace lt::slipstream;

#if 0
static void dump(const char * label, const Framing& rf)
{
    printf ("%s Framing: e:%d\tp:%d\tt:%ld\tsync:%s\n", label,
        rf.envelope_length, rf.payload_length, rf.source_timestamp,
        rf.sync ? "true" : "false");
}

static void manual_check(const Framing& input, const Framing& output)
{
    if (input.envelope_length != output.envelope_length) {
        printf ("Envelope lengths mismatch: %d vs %d\n",
        input.envelope_length, output.envelope_length);
    }
    if (input.payload_length != output.payload_length) {
        printf ("Payload lengths mismatch: %d vs %d\n",
        input.payload_length, output.payload_length);
    }
    if (input.source_timestamp != output.source_timestamp) {
        printf ("Source timestamps mismatch\n");
    }
    if (input.sync != output.sync) {
        printf ("Sync mismatch\n");
    }
}
#endif

static void roundtrip_encdec(const Framing& input)
{
    uint8_t buf[sizeof(Framing)];
    Framing output;

    input.encode(buf);
    BOOST_CHECK(output.decode(buf));

    BOOST_CHECK(input == output);
}

BOOST_AUTO_TEST_CASE(framing_encdec_roundtrip)
{
    roundtrip_encdec(Framing{0, 0, 0, false});
    roundtrip_encdec(Framing{0, 0, 0, true});

    roundtrip_encdec(Framing{13, 0, 0, false});
    roundtrip_encdec(Framing{13, 0, 0, true});

    roundtrip_encdec(Framing{7, 300, 1234567, false});
    roundtrip_encdec(Framing{7, 300, 1234567, true});

    roundtrip_encdec(Framing{(1<<12)-1, (1<<20)-1, std::numeric_limits<uint64_t>::max(), false});
    roundtrip_encdec(Framing{(1<<12)-1, (1<<20)-1, std::numeric_limits<uint64_t>::max(), true});

}

static void roundtrip_rw(const Framing& input)
{
    int fds[2];
    Framing output;

    pipe(fds);

    auto out = kj::FdOutputStream(kj::AutoCloseFd(fds[1]));
    auto&& [ write_success, nwritten ] = input.write(out);

    BOOST_CHECK(write_success);

    auto in = kj::FdInputStream(kj::AutoCloseFd(fds[0]));
    auto&& [ read_success, nread ] = output.read(in);

    BOOST_CHECK(read_success);

    BOOST_CHECK(nread == nwritten);

    BOOST_CHECK(input == output);
}

BOOST_AUTO_TEST_CASE(framing_roundtrip_rw)
{
    roundtrip_rw(Framing{0, 0, 0, 0, false});
    roundtrip_rw(Framing{0, 0, 0, 0, true});

    roundtrip_rw(Framing{13, 0, 0, 0, false});
    roundtrip_rw(Framing{13, 0, 0, 0, true});

    roundtrip_rw(Framing{7, 300, 1234567, 0x8f31, false});
    roundtrip_rw(Framing{7, 300, 1234567, 0x8f31, true});

    roundtrip_rw(Framing{(1<<12)-1, (1<<20)-1, std::numeric_limits<uint64_t>::max(), 0xFFFF, false});
    roundtrip_rw(Framing{(1<<12)-1, (1<<20)-1, std::numeric_limits<uint64_t>::max(), 0xFFFF, true});

}

namespace rc {

template <>
struct Arbitrary<Framing> {
    static Gen<Framing> arbitrary() {
        return gen::build<Framing>(
            gen::set(&Framing::envelope_length, gen::inRange(0, (1<<12)-1)),
            gen::set(&Framing::payload_length, gen::inRange(0, (1<<20)-1)),
            gen::set(&Framing::source_timestamp),
            gen::set(&Framing::checksum),
            gen::set(&Framing::sync));
    }
};

} // namespace rc

static void roundtrip_encdec_rc(const Framing& input)
{
    uint8_t buf[sizeof(Framing)];
    Framing output;

    input.encode(buf);
    RC_ASSERT(output.decode(buf));

    RC_ASSERT(input == output);
}

RC_BOOST_PROP(framing_roundtrip_encdec_rc, (Framing framing)) {
    roundtrip_encdec_rc(framing);
}

static void roundtrip_rw_rc(const Framing& input)
{
    int fds[2];
    Framing output;

    pipe(fds);

    auto out = kj::FdOutputStream(kj::AutoCloseFd(fds[1]));
    auto&& [ write_success, nwritten ] = input.write(out);

    RC_ASSERT(write_success);

    auto in = kj::FdInputStream(kj::AutoCloseFd(fds[0]));
    auto&& [ read_success, nread ] = output.read(in);

    RC_ASSERT(read_success);

    RC_ASSERT(nread == nwritten);

    RC_ASSERT(input == output);
}

RC_BOOST_PROP(framing_roundtrip_rw_rc, (Framing framing)) {
    roundtrip_rw_rc(framing);
}

max_stars_repo_name: kfish/slipstream

max_issues_repo_name: kfish/slipstream

max_forks_repo_name: kfish/slipstream


text:
// Copyright (c) 2014-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <validation.h>
#include <net.h>

#include <test/test_bitcoin.h>

#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)

static void TestBlockSubsidyChange(const Consensus::Params& consensusParams)
{
    const int blockSubsidyChangeHeight = consensusParams.SubsidyChangeHeight;
    const CAmount subsidyBeforeChange = 50 * COIN;
    const CAmount subsidyAfterChange = 1 * COIN;

    CAmount subsidy = GetBlockSubsidy(0, consensusParams);
    BOOST_CHECK_EQUAL(subsidyBeforeChange, subsidy);

    subsidy = GetBlockSubsidy(blockSubsidyChangeHeight-1, consensusParams);
    BOOST_CHECK_EQUAL(subsidyBeforeChange, subsidy);

    subsidy = GetBlockSubsidy(blockSubsidyChangeHeight, consensusParams);
    BOOST_CHECK_EQUAL(subsidyAfterChange, subsidy);

    subsidy = GetBlockSubsidy(blockSubsidyChangeHeight+1, consensusParams);
    BOOST_CHECK_EQUAL(subsidyAfterChange, subsidy);
}

static void TestBlockSubsidyChange(int nSubsidyChangeHeight)
{
    Consensus::Params consensusParams;
    consensusParams.SubsidyChangeHeight = nSubsidyChangeHeight;
    TestBlockSubsidyChange(consensusParams);
}

BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    TestBlockSubsidyChange(chainParams->GetConsensus()); // As in main
    TestBlockSubsidyChange(150); // As in regtest
    TestBlockSubsidyChange(1000); // Just another interval
}

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    CAmount nSum = 0;
    for (int nHeight = 0; nHeight < 50*1050000; ++nHeight) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
        BOOST_CHECK(nSubsidy == 50 * COIN || nSubsidy == 1 * COIN);
        nSum += nSubsidy;
        BOOST_CHECK(MoneyRange(nSum));
    }

    BOOST_CHECK_EQUAL(nSum, CAmount{8608386500000000});
}

static bool ReturnFalse() { return false; }
static bool ReturnTrue() { return true; }

BOOST_AUTO_TEST_CASE(test_combiner_all)
{
    boost::signals2::signal<bool (), CombinerAll> Test;
    BOOST_CHECK(Test());
    Test.connect(&ReturnFalse);
    BOOST_CHECK(!Test());
    Test.connect(&ReturnTrue);
    BOOST_CHECK(!Test());
    Test.disconnect(&ReturnFalse);
    BOOST_CHECK(Test());
    Test.disconnect(&ReturnTrue);
    BOOST_CHECK(Test());
}
BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: BlockStamp/bstnew

max_issues_repo_name: BlockStamp/bstnew

max_forks_repo_name: BlockStamp/bstnew


text:
#include "test.h"

#include <boost/variant.hpp>

namespace ts7 {
  namespace jsonrpc_playground {
    namespace any_collection {
      /**
       * @brief variant_visitor
       *
       * Visitor that calls the print() member function on the stored variant type.
       *
       * @since 1.0
       *
       * @author Tarek Schwarzinger <tarek.schwarzinger@googlemail.com>
       */
      struct variant_visitor : public boost::static_visitor<> {
          /**
           * @brief Print call
           *
           * Calls the print() member function on the variant.
           *
           * @tparam T The original type of the stored variant.
           *
           * @since 1.0
           *
           * @author Tarek Schwarzinger <tarek.schwarzinger@googlemail.com>
           */
          template<typename  T>
          void operator()(const T& t) const {
            t.print();
          }
      };

      /**
       * @brief variant example
       *
       * Example to demonstrate simple usage of the boost variant type.
       *
       * @since 1.0
       *
       * @author Tarek Schwarzinger <tarek.schwarzinger@googlemail.com>
       */
      void variant() {
        using variant_type = boost::variant<A, B, C<int>, C<double>, C<A>>;

        std::cout << "Variant print" << std::endl;

        variant_type v1 = A();
        variant_type v2 = B();
        variant_type v3 = C<int>();

        boost::apply_visitor(variant_visitor(), v1);
        boost::apply_visitor(variant_visitor(), v2);
        boost::apply_visitor(variant_visitor(), v3);

        std::cout << std::endl;
      }
    }
  }
}


text:
// test/trie/trie_base_iterator.cpp

#include <boost/test/unit_test.hpp>

#include <wordring/trie/trie_base.hpp>
#include <wordring/trie/trie_base_iterator.hpp>
#include <wordring/tree/tree_iterator.hpp>

#include <algorithm>
#include <fstream>
#include <memory>
#include <random>
#include <sstream>
#include <string>
#include <vector>

namespace
{
	using wordring::detail::trie_node;
	using base_iterator = wordring::detail::const_trie_base_iterator<std::vector<trie_node, std::allocator<trie_node>> const>;
	using base_trie     = wordring::detail::trie_base<>;

	class test_iterator : public base_iterator
	{
	public:
		using base_type = base_iterator;

		using base_type::index_type;

		using base_type::find;
		using base_type::mother;
		using base_type::base;
		using base_type::limit;
		using base_type::has_null;
		using base_type::has_sibling;

		using base_type::m_c;
		using base_type::m_index;

	public:
		test_iterator() : base_type() {}

		test_iterator(container& c, index_type index)
			: base_type(c, index) {}

		test_iterator(base_type const& base)
			: base_type(base) {}
	};

	class test_trie : public base_trie
	{
	public:
		using base_type = base_trie;

		using base_type::is_tail;
		using base_type::has_child;
		using base_type::has_null;
		using base_type::has_sibling;
		using base_type::at;
		using base_type::add;

		using base_type::m_c;

	public:
		/*! 保持する文字列の数を数える
		- size()と同じ数を返せば良好。
		*/
		std::size_t count() const
		{
			using namespace wordring;
			std::size_t n = 0;

			auto it1 = tree_iterator<decltype(begin())>(begin());
			auto it2 = tree_iterator<decltype(begin())>();

			while (it1 != it2)
			{
				if (it1.base()) ++n;
				++it1;
			}

			return n;
		}
	};
}

BOOST_AUTO_TEST_SUITE(const_trie_base_iterator__test)

// ----------------------------------------------------------------------------
// const_trie_base_iterator
// ----------------------------------------------------------------------------

// const_trie_base_iterator()
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__construct__1)
{
	using namespace wordring;

	test_iterator it;
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__construct__2)
{
	using namespace wordring;

	test_iterator it{};
}

// const_trie_base_iterator(container& c, index_type index)
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__construct__3)
{
	using namespace wordring;

	test_trie trie{};
	test_iterator it(trie.m_c, 1);
}

// operator bool() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__bool__1)
{
	using namespace wordring;

	test_trie trie{};
	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ 0,  0 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	// 親状態2に対してnull_valueによる遷移を追加
	trie.m_c.insert(trie.m_c.end(), 252, { 0, 0 });
	trie.m_c[261] = wordring::detail::trie_node{ 0, 2 };

	BOOST_CHECK(test_iterator(trie.m_c, 1) == false);
	BOOST_CHECK(test_iterator(trie.m_c, 2) == true);
	BOOST_CHECK(test_iterator(trie.m_c, 3) == true);
	BOOST_CHECK(test_iterator(trie.m_c, 4) == false);
	BOOST_CHECK(test_iterator(trie.m_c, 5) == true);
	BOOST_CHECK(test_iterator(trie.m_c, 6) == false);
	BOOST_CHECK(test_iterator(trie.m_c, 7) == true);
	BOOST_CHECK(test_iterator(trie.m_c, 8) == true);
	BOOST_CHECK(test_iterator(trie.m_c, 9) == true);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__bool__2)
{
	using namespace wordring;

	test_trie trie{};

	trie.insert(std::string("a"));
	BOOST_CHECK(trie.count() == 1);
	BOOST_CHECK(trie.find(std::string("a")).operator bool());
	BOOST_CHECK(trie.find(std::string("a")).operator!() == false);

	BOOST_CHECK(trie.find(std::string("c")).operator bool() == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__bool__3)
{
	using namespace wordring;

	test_trie trie{};

	trie.insert(std::string("a"));
	trie.insert(std::string("ab"));
	BOOST_CHECK(trie.count() == 2);
	BOOST_CHECK(trie.find(std::string("a")).operator bool());
	BOOST_CHECK(trie.find(std::string("ab")).operator bool());
	BOOST_CHECK(trie.find(std::string("a")).operator!() == false);
	BOOST_CHECK(trie.find(std::string("ab")).operator!() == false);

	BOOST_CHECK(trie.find(std::string("c")).operator bool() == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__bool__4)
{
	using namespace wordring;

	test_trie trie{};

	trie.insert(std::string("ab"));
	trie.insert(std::string("a"));
	BOOST_CHECK(trie.count() == 2);
	BOOST_CHECK(trie.find(std::string("a")).operator bool());
	BOOST_CHECK(trie.find(std::string("ab")).operator bool());
	BOOST_CHECK(trie.find(std::string("a")).operator!() == false);
	BOOST_CHECK(trie.find(std::string("ab")).operator!() == false);

	BOOST_CHECK(trie.find(std::string("c")).operator bool() == false);
}

// value_type operator*() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__reference__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	BOOST_CHECK(*test_iterator(trie.m_c, 2) == 1);
	BOOST_CHECK(*test_iterator(trie.m_c, 3) == 2);
	BOOST_CHECK(*test_iterator(trie.m_c, 4) == 3);
	BOOST_CHECK(*test_iterator(trie.m_c, 5) == 0);
	BOOST_CHECK(*test_iterator(trie.m_c, 6) == 1);
	BOOST_CHECK(*test_iterator(trie.m_c, 7) == 2);
	BOOST_CHECK(*test_iterator(trie.m_c, 8) == 3);
	BOOST_CHECK(*test_iterator(trie.m_c, 9) == 4);
}

// const_trie_base_iterator operator[](std::uint16_t label) const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__at__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it2 = test_iterator(trie.m_c, 1)[1];
	BOOST_CHECK(it2.m_index == 2);

	test_iterator it3 = test_iterator(trie.m_c, 1)[2];
	BOOST_CHECK(it3.m_index == 3);

	test_iterator it4 = test_iterator(trie.m_c, 1)[3];
	BOOST_CHECK(it4.m_index == 4);

	test_iterator it5 = test_iterator(trie.m_c, 2)[0];
	BOOST_CHECK(it5.m_index == 5);

	test_iterator it8 = test_iterator(trie.m_c, 2)[3];
	BOOST_CHECK(it8.m_index == 8);

	test_iterator it6 = test_iterator(trie.m_c, 4)[1];
	BOOST_CHECK(it6.m_index == 6);

	test_iterator it9 = test_iterator(trie.m_c, 4)[4];
	BOOST_CHECK(it9.m_index == 9);

	test_iterator it7 = test_iterator(trie.m_c, 6)[2];
	BOOST_CHECK(it7.m_index == 7);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__at__2)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	BOOST_CHECK(test_iterator(trie.m_c, 1)[0] == test_iterator(trie.m_c, 1).end());
	BOOST_CHECK(test_iterator(trie.m_c, 1)[4] == test_iterator(trie.m_c, 1).end());

	BOOST_CHECK(test_iterator(trie.m_c, 2)[4] == test_iterator(trie.m_c, 2).end());

	BOOST_CHECK(test_iterator(trie.m_c, 3)[0] == test_iterator(trie.m_c, 3).end());
	BOOST_CHECK(test_iterator(trie.m_c, 3)[1] == test_iterator(trie.m_c, 3).end());

	BOOST_CHECK(test_iterator(trie.m_c, 4)[0] == test_iterator(trie.m_c, 4).end());
	BOOST_CHECK(test_iterator(trie.m_c, 4)[2] == test_iterator(trie.m_c, 4).end());

	BOOST_CHECK(test_iterator(trie.m_c, 5)[0] == test_iterator(trie.m_c, 5).end());
	BOOST_CHECK(test_iterator(trie.m_c, 5)[1] == test_iterator(trie.m_c, 5).end());

	BOOST_CHECK(test_iterator(trie.m_c, 8)[0] == test_iterator(trie.m_c, 8).end());
	BOOST_CHECK(test_iterator(trie.m_c, 8)[1] == test_iterator(trie.m_c, 8).end());

	BOOST_CHECK(test_iterator(trie.m_c, 6)[0] == test_iterator(trie.m_c, 6).end());
	BOOST_CHECK(test_iterator(trie.m_c, 6)[1] == test_iterator(trie.m_c, 6).end());

	BOOST_CHECK(test_iterator(trie.m_c, 9)[0] == test_iterator(trie.m_c, 9).end());
	BOOST_CHECK(test_iterator(trie.m_c, 9)[1] == test_iterator(trie.m_c, 9).end());

	BOOST_CHECK(test_iterator(trie.m_c, 7)[0] == test_iterator(trie.m_c, 7).end());
	BOOST_CHECK(test_iterator(trie.m_c, 7)[1] == test_iterator(trie.m_c, 7).end());
}

// const_trie_base_iterator& operator++()
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__increment__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	BOOST_CHECK(*++test_iterator(trie.m_c, 2) == 2);
	BOOST_CHECK(*++test_iterator(trie.m_c, 3) == 3);
	BOOST_CHECK(*++test_iterator(trie.m_c, 5) == 3);
	BOOST_CHECK(*++test_iterator(trie.m_c, 6) == 4);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__increment__2)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it1 = ++test_iterator(trie.m_c, 4);
	BOOST_CHECK(it1.m_index == 0);

	test_iterator it2 = ++test_iterator(trie.m_c, 8);
	BOOST_CHECK(it2.m_index == 0);

	test_iterator it3 = ++test_iterator(trie.m_c, 9);
	BOOST_CHECK(it3.m_index == 0);
}

// const_trie_base_iterator operator++(int)
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__increment__3)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	auto it2 = test_iterator(trie.m_c, 2);
	BOOST_CHECK(*it2++ == 1); // a
	BOOST_CHECK(*it2 == 2);   // b

	BOOST_CHECK(*it2++ == 2); // b
	BOOST_CHECK(*it2 == 3);   // c

	BOOST_CHECK(*it2++ == 3); // c

	auto it5 = test_iterator(trie.m_c, 5);
	BOOST_CHECK(*it5++ == 0); // #
	BOOST_CHECK(*it5 == 3);   // c

	BOOST_CHECK(*it5++ == 3); // c

	auto it6 = test_iterator(trie.m_c, 6);
	BOOST_CHECK(*it6++ == 1); // a
	BOOST_CHECK(*it6 == 4);   // d

	BOOST_CHECK(*it6++ == 4); // d
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__increment__4)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	auto it4 = test_iterator(trie.m_c, 4);
	BOOST_CHECK(*it4++ == 3);
	BOOST_CHECK(it4.m_index == 0);

	auto it8 = test_iterator(trie.m_c, 8);
	BOOST_CHECK(*it8++ == 3);
	BOOST_CHECK(it8.m_index == 0);

	auto it9 = test_iterator(trie.m_c, 9);
	BOOST_CHECK(*it9++ == 4);
	BOOST_CHECK(it9.m_index == 0);
}

// String string() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__string__1)
{
	using namespace wordring;

	test_trie trie{};

	std::string s;

	test_iterator it0{};
	it0.string(s);
	BOOST_CHECK(s == "");

	test_iterator it1 = trie.insert(std::string("\1"));
	it1.string(s);
	BOOST_CHECK(s == "\1");

	test_iterator it2 = trie.insert(std::string("\1\3"));
	it2.string(s);
	BOOST_CHECK(s == "\1\3");

	test_iterator it3 = trie.insert(std::string("\2"));
	it3.string(s);
	BOOST_CHECK(s == "\2");

	test_iterator it4 = trie.insert(std::string("\3\1\2"));
	it4.string(s);
	BOOST_CHECK(s == "\3\1\2");

	test_iterator it5 = trie.insert(std::string("\3\4"));
	it5.string(s);
	BOOST_CHECK(s == "\3\4");
}

// std::optional<const_trie_base_iterator> parent() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__parent__1)
{
	using namespace wordring;

	test_trie trie{};

	test_iterator it1 = trie.insert(std::string("\1"));
	BOOST_CHECK(it1.parent().parent() == trie.cend());

	test_iterator it2 = trie.insert(std::string("\1\3"));
	BOOST_CHECK(*it2.parent() == 1);

	test_iterator it3 = trie.insert(std::string("\2"));
	BOOST_CHECK(it3.parent().parent() == trie.cend());

	test_iterator it4 = trie.insert(std::string("\3\1\2"));
	BOOST_CHECK(*it4.parent() == 1);
	BOOST_CHECK(*it4.parent().parent() == 3);

	test_iterator it5 = trie.insert(std::string("\3\4"));
	BOOST_CHECK(*it5.parent() == 3);
	BOOST_CHECK(it5.parent().parent().parent() == trie.cend());
}

// const_trie_base_iterator begin() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__begin__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it1 = test_iterator(trie.m_c, 1).begin();
	BOOST_CHECK(it1.m_index == 1 + 1);

	test_iterator it2 = test_iterator(trie.m_c, 2).begin();
	BOOST_CHECK(it2.m_index == 5 + 0);

	test_iterator it4 = test_iterator(trie.m_c, 4).begin();
	BOOST_CHECK(it4.m_index == 5 + 1);

	test_iterator it6 = test_iterator(trie.m_c, 6).begin();
	BOOST_CHECK(it6.m_index == 5 + 2);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__begin__2)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it5 = test_iterator(trie.m_c, 5).begin();
	BOOST_CHECK(it5.m_index == 0);

	test_iterator it8 = test_iterator(trie.m_c, 8).begin();
	BOOST_CHECK(it8.m_index == 0);

	test_iterator it3 = test_iterator(trie.m_c, 3).begin();
	BOOST_CHECK(it3.m_index == 0);

	test_iterator it7 = test_iterator(trie.m_c, 7).begin();
	BOOST_CHECK(it7.m_index == 0);

	test_iterator it9 = test_iterator(trie.m_c, 9).begin();
	BOOST_CHECK(it9.m_index == 0);
}

// const_trie_base_iterator end() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__end__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it1 = test_iterator(trie.m_c, 1).end();
	BOOST_CHECK(it1.m_index == 0);

	test_iterator it2 = test_iterator(trie.m_c, 2).end();
	BOOST_CHECK(it2.m_index == 0);

	test_iterator it4 = test_iterator(trie.m_c, 4).end();
	BOOST_CHECK(it4.m_index == 0);

	test_iterator it6 = test_iterator(trie.m_c, 6).end();
	BOOST_CHECK(it6.m_index == 0);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__end__2)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{  0, 0 }, // 0
		{  1, 0 }, // 1
		{  5, 1 }, // 2
		{ -3, 1 }, // 3
		{  5, 1 }, // 4
		{ -1, 2 }, // 5
		{  5, 4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	test_iterator it5 = test_iterator(trie.m_c, 5).end();
	BOOST_CHECK(it5.m_index == 0);

	test_iterator it8 = test_iterator(trie.m_c, 8).end();
	BOOST_CHECK(it8.m_index == 0);

	test_iterator it3 = test_iterator(trie.m_c, 3).end();
	BOOST_CHECK(it3.m_index == 0);

	test_iterator it7 = test_iterator(trie.m_c, 7).end();
	BOOST_CHECK(it7.m_index == 0);

	test_iterator it9 = test_iterator(trie.m_c, 9).end();
	BOOST_CHECK(it9.m_index == 0);
}

// index_type find(index_type first, index_type last, index_type check)
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__find__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	auto it1 = test_iterator(trie.m_c, 1);

	BOOST_CHECK(it1.find(1, 256, 1) == 2);
	BOOST_CHECK(it1.find(2, 256, 1) == 2);
	BOOST_CHECK(it1.find(3, 256, 1) == 3);
	BOOST_CHECK(it1.find(4, 256, 1) == 4);
	BOOST_CHECK(it1.find(5, 256, 1) == 0);
}

// index_type mother() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__mother__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0, 0 },  // 0
		{ 1, 0 },  // 1
		{ 5, -1 }, // 2
		{ 5, 1 }   // 3
	};

	BOOST_CHECK(test_iterator(trie.m_c, 1).mother() == 0);
	BOOST_CHECK(test_iterator(trie.m_c, 2).mother() == 0);
	BOOST_CHECK(test_iterator(trie.m_c, 3).mother() == 1);
}

// index_type base() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__base__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	BOOST_CHECK(test_iterator(trie.m_c, 2).base() == 1);
	BOOST_CHECK(test_iterator(trie.m_c, 3).base() == 1);
	BOOST_CHECK(test_iterator(trie.m_c, 4).base() == 1);
	BOOST_CHECK(test_iterator(trie.m_c, 5).base() == 5);
	BOOST_CHECK(test_iterator(trie.m_c, 6).base() == 5);
	BOOST_CHECK(test_iterator(trie.m_c, 7).base() == 5);
	BOOST_CHECK(test_iterator(trie.m_c, 8).base() == 5);
	BOOST_CHECK(test_iterator(trie.m_c, 9).base() == 5);
}

// index_type limit() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__tail__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0,  0 }, // 0
		{ 1,  0 }, // 1
		{ 5,  1 }, // 2
		{ -3, 1 }, // 3
		{ 5,  1 }, // 4
		{ -1, 2 }, // 5
		{ 5,  4 }, // 6
		{ -4, 6 }, // 7
		{ -2, 2 }, // 8
		{ -5, 4 }  // 9
	};

	BOOST_CHECK(test_iterator(trie.m_c, 1).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 2).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 3).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 4).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 5).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 6).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 7).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 8).limit() == 10);
	BOOST_CHECK(test_iterator(trie.m_c, 9).limit() == 10);
}

// bool has_null() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__has_null__1)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0, 0 }, // 0
		{ 1, 0 }, // 1
		{ 1, 1 }, // 2
		{ 0, 0 }, // 3
		{ 0, 2 }  // 4
	};
	trie.m_c.insert(trie.m_c.cend(), 252, { 0, 0 });
	trie.m_c.push_back({ 0, 2 }); // 257
	BOOST_CHECK(trie.m_c.size() == 258);

	BOOST_CHECK(test_iterator(trie.m_c, 1).has_null() == false);
	BOOST_CHECK(test_iterator(trie.m_c, 2).has_null());
	BOOST_CHECK(test_iterator(trie.m_c, 3).has_null() == false);
	BOOST_CHECK(test_iterator(trie.m_c, 4).has_null() == false);
	BOOST_CHECK(test_iterator(trie.m_c, 5).has_null() == false);
	BOOST_CHECK(test_iterator(trie.m_c, 257).has_null() == false);
}

// bool has_sibling() const
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__has_sibling__1)
{
	using namespace wordring;

	test_trie trie{};

	std::vector<std::string> v{ "a", "ac", "b", "cab", "cd" };
	trie.assign(v.begin(), v.end());

	test_iterator it1 = trie.search(std::string("a"));
	BOOST_CHECK(it1.has_sibling());

	test_iterator it2 = trie.search(std::string("b"));
	BOOST_CHECK(it2.has_sibling());

	test_iterator it3 = trie.search(std::string("c"));
	BOOST_CHECK(it3.has_sibling());
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__has_sibling__2)
{
	using namespace wordring;

	test_trie trie{};

	std::vector<std::string> v{ "a", "ac", "b", "cab", "cd" };
	trie.assign(v.begin(), v.end());

	test_iterator it1 = trie.search(std::string("cab"));
	BOOST_CHECK(it1.has_sibling() == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__has_sibling__3)
{
	using namespace wordring;

	test_trie trie{};

	std::vector<std::string> v{ "a", "ac", "b", "cab", "cd" };
	trie.assign(v.begin(), v.end());

	test_iterator it1 = trie.search(std::string("ac"));
	BOOST_CHECK(it1.has_sibling() == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__has_sibling__4)
{
	using namespace wordring;

	test_trie trie{};

	trie.insert(std::string("a"));
	trie.insert(std::string("ac"));

	test_iterator it1 = trie.search(std::string("a"));
	BOOST_CHECK(it1.has_sibling() == false);

	test_iterator it2 = trie.search(std::string("ac"));
	BOOST_CHECK(it2.has_sibling() == false);
}

// inline bool operator==(const_trie_base_iterator<Container1> const& lhs, const_trie_base_iterator<Container1> const& rhs)
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__equal__1)
{
	using namespace wordring;

	test_trie trie{};
	BOOST_CHECK(test_iterator(trie.m_c, 1) == test_iterator(trie.m_c, 1));
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__equal__2)
{
	using namespace wordring;

	BOOST_CHECK(test_iterator() == test_iterator());
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__equal__3)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0, 0 }, // 0
		{ 1, 0 }, // 1
		{ 5, 1 }, // 2
	};

	BOOST_CHECK((test_iterator(trie.m_c, 1) == test_iterator(trie.m_c, 2)) == false);
}

// inline bool operator!=(const_trie_base_iterator<Container1> const& lhs, const_trie_base_iterator<Container1> const& rhs)
BOOST_AUTO_TEST_CASE(const_trie_base_iterator__not_equal__1)
{
	using namespace wordring;

	test_trie trie{};
	BOOST_CHECK((test_iterator(trie.m_c, 1) != test_iterator(trie.m_c, 1)) == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__not_equal__2)
{
	using namespace wordring;

	BOOST_CHECK((test_iterator() != test_iterator()) == false);
}

BOOST_AUTO_TEST_CASE(const_trie_base_iterator__not_equal__3)
{
	using namespace wordring;

	test_trie trie{};

	trie.m_c = {
		{ 0, 0 }, // 0
		{ 1, 0 }, // 1
		{ 5, 1 }, // 2
	};

	BOOST_CHECK(test_iterator(trie.m_c, 1) != test_iterator(trie.m_c, 2));
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: wordring/wordring

max_issues_repo_name: wordring/libwordring

max_forks_repo_name: wordring/libwordring


text:
/**
 * @CopyRight:
 * FISCO-BCOS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FISCO-BCOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FISCO-BCOS.  If not, see <http://www.gnu.org/licenses/>
 * (c) 2016-2018 fisco-dev contributors.
 *
 * @brief
 *
 * @file BlockChainImp.cpp
 * @author:
 * @date 2018-09-21
 */
#include "libdevcrypto/CryptoInterface.h"
#include <libblockchain/BlockChainImp.h>
#include <libblockverifier/ExecutiveContext.h>
#include <libconfig/GlobalConfigure.h>
#include <libdevcore/CommonData.h>
#include <libethcore/Block.h>
#include <libethcore/BlockHeader.h>
#include <libethcore/Transaction.h>
#include <libstorage/Common.h>
#include <libstorage/MemoryTable.h>
#include <libstoragestate/StorageState.h>
#include <libstoragestate/StorageStateFactory.h>
#include <test/tools/libutils/Common.h>
#include <test/tools/libutils/TestOutputHelper.h>
#include <test/unittests/libethcore/FakeBlock.h>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
#include <unordered_map>

using namespace dev;
using namespace dev::eth;
using namespace dev::storage;
using namespace dev::blockchain;
using namespace dev::blockverifier;
using namespace dev::storagestate;
namespace dev
{
namespace test
{
//   const std::string m_extraDbName_currentState = "currentState";
//   const std::string SYS_KEY_CURRENT_NUMBER = "currentNumber";
//   const std::string SYS_VALUE = "value";
//   const std::string SYS_VALUE = "value";
//   const std::string m_txHash2Block = "txHash2Block";
//   const std::string m_number2hash = "number2hash";
//   const std::string m_hash2Block = "hash2Block";

#if 0
static std::string const c_commonHash =
    "067150c07dab4facb7160e075548007e067150c07dab4facb7160e075548007e";
#endif
// TODO: compatibility support
static std::string const c_commonHash =
    "0b9702045ea4b58d1bdae2f802f57ac22c6b63ce571777da8586337c69834ad3";
static std::string const c_commonHashPrefix = std::string("0x").append(c_commonHash);

class MockTable : public dev::storage::MemoryTable<Serial>
{
public:
    typedef std::shared_ptr<MockTable> Ptr;

    MockTable()
    {
        m_fakeStorage[SYS_CURRENT_STATE] = std::unordered_map<std::string, Entry::Ptr>();
        m_fakeStorage[SYS_NUMBER_2_HASH] = std::unordered_map<std::string, Entry::Ptr>();
        m_fakeStorage[SYS_HASH_2_BLOCK] = std::unordered_map<std::string, Entry::Ptr>();
        m_fakeStorage[SYS_TX_HASH_2_BLOCK] = std::unordered_map<std::string, Entry::Ptr>();
    }

    Entries::ConstPtr select(const std::string& key, Condition::Ptr) override
    {
        Entries::Ptr entries = std::make_shared<Entries>();

        if (m_fakeStorage.find(m_table) != m_fakeStorage.end() &&
            m_fakeStorage[m_table].find(key) != m_fakeStorage[m_table].end())
        {
            auto entry = m_fakeStorage[m_table][key];
            entries->addEntry(entry);
        }
        return entries;
    }

    int insert(const std::string& key, Entry::Ptr entry, AccessOptions::Ptr, bool) override
    {
        m_fakeStorage[m_table].insert(std::make_pair(key, entry));
        return 0;
    }

    int update(
        const std::string& key, Entry::Ptr entry, Condition::Ptr, AccessOptions::Ptr) override
    {
        m_fakeStorage[m_table][key] = entry;
        return 0;
    }

    void commitDB(int64_t _blockNumber)
    {
        for (auto pmainKey : m_fakeStorage)
        {
            for (auto psubKey : pmainKey.second)
            {
                auto entry = psubKey.second;
                entry->setNum(_blockNumber);
                entry->setField("_num_", std::to_string(_blockNumber));
            }
        }
    }

    std::string m_table;
    std::unordered_map<std::string, std::unordered_map<std::string, Entry::Ptr>> m_fakeStorage;
};

class MockMemoryTableFactory : public dev::storage::MemoryTableFactory
{
public:
    MockMemoryTableFactory() {}

    Table::Ptr openTable(const std::string& _table, bool = true, bool = false) override
    {
        UpgradableGuard l(x_name2Table);
        if (m_name2Table.count(_table))
        {
            return m_name2Table[_table];
        }
        UpgradeGuard ul(l);
        auto table = std::make_shared<MockTable>();
        table->m_table = _table;
        m_name2Table[_table] = table;
        return table;
    }

    void commitDB(h256 const&, int64_t _blockNumber) override
    {
        ReadGuard l(x_name2Table);
        for (auto it : m_name2Table)
        {
            it.second->commitDB(_blockNumber);
        }
    }

    std::map<std::string, std::shared_ptr<MockTable>> m_name2Table;
    mutable SharedMutex x_name2Table;
};

class MockBlockChainImp : public BlockChainImp
{
public:
    std::shared_ptr<dev::storage::TableFactory> getMemoryTableFactory(int64_t) override
    {
        return m_memoryTableFactory;
    }

    void setMemoryTableFactory(std::shared_ptr<dev::storage::TableFactory> _m)
    {
        m_memoryTableFactory = _m;
    }
    std::shared_ptr<dev::storage::TableFactory> m_memoryTableFactory;
};

class MockState : public StorageState
{
public:
    MockState() : StorageState(u256(0)) {}

    void dbCommit(h256 const&, int64_t) override {}
};

struct EmptyFixture
{
    EmptyFixture()
    {
        m_executiveContext = std::make_shared<ExecutiveContext>();
        m_blockChainImp = std::make_shared<MockBlockChainImp>();
        mockMemoryTableFactory = std::make_shared<MockMemoryTableFactory>();
        m_storageStateFactory = std::make_shared<StorageStateFactory>(0x0);

        m_blockChainImp->setMemoryTableFactory(mockMemoryTableFactory);
        m_blockChainImp->setStateFactory(m_storageStateFactory);
        m_executiveContext->setMemoryTableFactory(mockMemoryTableFactory);
        m_executiveContext->setState(std::make_shared<MockState>());

        auto initParam = std::make_shared<dev::ledger::LedgerParam>();
        initParam->mutableGenesisMark() = "";
        initParam->mutableConsensusParam().sealerList = dev::h512s();
        initParam->mutableConsensusParam().observerList = dev::h512s();
        initParam->mutableConsensusParam().consensusType = "";
        initParam->mutableStorageParam().type = "";
        initParam->mutableStateParam().type = "";
        initParam->mutableConsensusParam().maxTransactions = 0;
        initParam->mutableTxParam().txGasLimit = 0;
        initParam->mutableGenesisParam().timeStamp = 0;
        m_blockChainImp->checkAndBuildGenesisBlock(initParam);
    }

    std::shared_ptr<MockMemoryTableFactory> mockMemoryTableFactory;
    std::shared_ptr<MockBlockChainImp> m_blockChainImp;
    std::shared_ptr<ExecutiveContext> m_executiveContext;
    std::shared_ptr<StorageStateFactory> m_storageStateFactory;
};

struct MemoryTableFactoryFixture : EmptyFixture
{
    MemoryTableFactoryFixture()
    {
        m_version = g_BCOSConfig.version();
        m_supportedVersion = g_BCOSConfig.supportedVersion();

        g_BCOSConfig.setSupportedVersion("2.2.0", V2_2_0);
        m_fakeBlock = std::make_shared<FakeBlock>(5);
        insertGenesisBlock(m_fakeBlock);
    }

    void insertGenesisBlock(std::shared_ptr<FakeBlock> _fakeBlock)
    {
        Entry::Ptr entry = std::make_shared<Entry>();
        entry->setField("value", "0");
        auto table = mockMemoryTableFactory->openTable(SYS_CURRENT_STATE);
        table->update(SYS_KEY_CURRENT_NUMBER, entry, nullptr, nullptr);

        entry = std::make_shared<Entry>();
        entry->setField("value",
            boost::lexical_cast<std::string>(_fakeBlock->getBlock()->transactions()->size()));
        table = mockMemoryTableFactory->openTable(SYS_CURRENT_STATE);
        table->update(SYS_KEY_TOTAL_TRANSACTION_COUNT, entry, nullptr, nullptr);

        entry = std::make_shared<Entry>();
        entry->setField("value", c_commonHashPrefix);
        table = mockMemoryTableFactory->openTable(SYS_NUMBER_2_HASH);
        table->update("0", entry, nullptr, nullptr);

        entry = std::make_shared<Entry>();
        bytes encodedData;
        _fakeBlock->m_block->encode(encodedData);
        entry->setField("value", encodedData.data(), encodedData.size());
        table = mockMemoryTableFactory->openTable(SYS_HASH_2_BLOCK);
        table->update(c_commonHash, entry, nullptr, nullptr);

        entry = std::make_shared<Entry>();
        entry->setField("value", "0");
        entry->setField("index", "0");
        table = mockMemoryTableFactory->openTable(SYS_TX_HASH_2_BLOCK);
        table->update(c_commonHash, entry, nullptr, nullptr);
    }

    ~MemoryTableFactoryFixture()
    {
        g_BCOSConfig.setSupportedVersion(m_supportedVersion, m_version);
    }

    std::shared_ptr<FakeBlock> m_fakeBlock;
    dev::VERSION m_version;
    std::string m_supportedVersion;
};

BOOST_FIXTURE_TEST_SUITE(BlockChainImpl, MemoryTableFactoryFixture)

BOOST_AUTO_TEST_CASE(emptyChain)
{
    // special case
    EmptyFixture empty;

    BOOST_CHECK_EQUAL(empty.m_blockChainImp->number(), 0);
    BOOST_CHECK_EQUAL(empty.m_blockChainImp->totalTransactionCount().first, 0);
    BOOST_CHECK_EQUAL(empty.m_blockChainImp->totalTransactionCount().second, 0);
    BOOST_CHECK_NO_THROW(empty.m_blockChainImp->getCode(Address(0x0)));

    /// modify the hash of the empty block since "timestamp" has been added into groupMark
    BOOST_CHECK_EQUAL(empty.m_blockChainImp->numberHash(0),
        h256("0x2d1c730a81f92c9888f508a9c1a4cdeed7063b831f1a21d61a4d6d97584fc260"));

    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getBlockByHash(h256(c_commonHashPrefix)), std::shared_ptr<Block>());
    BOOST_CHECK_EQUAL(
        (empty.m_blockChainImp->getLocalisedTxByHash(h256(c_commonHashPrefix)))->blockHash(),
        h256(0));
    BOOST_CHECK_EQUAL(
        (empty.m_blockChainImp->getLocalisedTxByHash(h256(c_commonHashPrefix)))->blockNumber(), -1);
    BOOST_CHECK_EQUAL(
        *(empty.m_blockChainImp->getTxByHash(h256(c_commonHashPrefix))), Transaction());
    BOOST_CHECK_EQUAL(
        crypto::Hash(
            empty.m_blockChainImp->getTransactionReceiptByHash(h256(c_commonHashPrefix))->rlp()),
        crypto::Hash(TransactionReceipt().rlp()));
    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getLocalisedTxReceiptByHash(h256(c_commonHashPrefix))->hash(),
        h256(0));
}

BOOST_AUTO_TEST_CASE(number2hash)
{
    BOOST_CHECK_EQUAL(m_blockChainImp->numberHash(0), h256(c_commonHashPrefix));
}

BOOST_AUTO_TEST_CASE(getCode)
{
    auto code = m_blockChainImp->getCode(Address(0x0));
    BOOST_CHECK_EQUAL(code.size(), 0);
}

BOOST_AUTO_TEST_CASE(getBlockByHash)
{
    std::shared_ptr<dev::eth::Block> bptr =
        m_blockChainImp->getBlockByHash(h256(c_commonHashPrefix));

    BOOST_CHECK_EQUAL(bptr->getTransactionSize(), m_fakeBlock->getBlock()->getTransactionSize());
    BOOST_CHECK_EQUAL(bptr->getTransactionSize(), 5);
}

BOOST_AUTO_TEST_CASE(getBlockRLPByNumber)
{
    std::shared_ptr<bytes> bRLPptr = m_blockChainImp->getBlockRLPByNumber(0);

    std::shared_ptr<dev::eth::Block> bptr =
        m_blockChainImp->getBlockByHash(h256(c_commonHashPrefix));
    BOOST_CHECK(bptr->rlp() == *bRLPptr);
}

BOOST_AUTO_TEST_CASE(getLocalisedTxByHash)
{
    LocalisedTransaction::Ptr localizedTx =
        m_blockChainImp->getLocalisedTxByHash(h256(c_commonHashPrefix));
    BOOST_CHECK_EQUAL(localizedTx->tx()->hash(), (*m_fakeBlock->m_transaction)[0]->hash());
}

BOOST_AUTO_TEST_CASE(getTxByHash)
{
    Transaction::Ptr tx = m_blockChainImp->getTxByHash(h256(c_commonHashPrefix));
    BOOST_CHECK_EQUAL(tx->hash(), (*m_fakeBlock->m_transaction)[0]->hash());
}

BOOST_AUTO_TEST_CASE(getTransactionReceiptByHash)
{
    auto txReceipt = m_blockChainImp->getTransactionReceiptByHash(h256(c_commonHashPrefix));

    BOOST_CHECK_EQUAL(crypto::Hash(txReceipt->rlp()),
        crypto::Hash((*m_fakeBlock->m_transactionReceipt)[0]->rlp()));
}

BOOST_AUTO_TEST_CASE(getLocalisedTxReceiptByHash)
{
    auto localisedTxReceipt =
        m_blockChainImp->getLocalisedTxReceiptByHash(h256(c_commonHashPrefix));

    BOOST_CHECK_EQUAL(localisedTxReceipt->hash(), h256(c_commonHashPrefix));
}

BOOST_AUTO_TEST_CASE(commitBlock)
{
    auto fakeBlock2 = std::make_shared<FakeBlock>(10);
    fakeBlock2->getBlock()->header().setNumber(m_blockChainImp->number());
    auto commitResult = m_blockChainImp->commitBlock(fakeBlock2->getBlock(), m_executiveContext);
    BOOST_CHECK(commitResult == CommitResult::ERROR_NUMBER);

    fakeBlock2->getBlock()->header().setNumber(m_blockChainImp->number() + 1);
    commitResult = m_blockChainImp->commitBlock(fakeBlock2->getBlock(), m_executiveContext);
    BOOST_CHECK(commitResult == CommitResult::ERROR_PARENT_HASH);

    fakeBlock2->getBlock()->header().setParentHash(
        m_blockChainImp->numberHash(m_blockChainImp->number()));
    commitResult = m_blockChainImp->commitBlock(fakeBlock2->getBlock(), m_executiveContext);
    BOOST_CHECK(commitResult == CommitResult::OK);
    BOOST_CHECK_EQUAL(m_blockChainImp->number(), 1);
    BOOST_CHECK_EQUAL(m_blockChainImp->totalTransactionCount().first, 15);
    BOOST_CHECK_EQUAL(m_blockChainImp->totalTransactionCount().second, 1);

    auto fakeBlock3 = std::make_shared<FakeBlock>(15);
    fakeBlock3->getBlock()->header().setNumber(m_blockChainImp->number() + 1);
    fakeBlock3->getBlock()->header().setParentHash(
        m_blockChainImp->numberHash(m_blockChainImp->number()));
    commitResult = m_blockChainImp->commitBlock(fakeBlock3->getBlock(), m_executiveContext);
    BOOST_CHECK(commitResult == CommitResult::OK);
    BOOST_CHECK_EQUAL(m_blockChainImp->number(), 2);
    BOOST_CHECK_EQUAL(m_blockChainImp->totalTransactionCount().first, 30);
    BOOST_CHECK_EQUAL(m_blockChainImp->totalTransactionCount().second, 2);
}

BOOST_AUTO_TEST_CASE(query)
{
    dev::h512s sealerList = m_blockChainImp->sealerList();
    BOOST_CHECK_EQUAL(sealerList.size(), 0);
    dev::h512s observerList = m_blockChainImp->observerList();
    BOOST_CHECK_EQUAL(observerList.size(), 0);
}

BOOST_FIXTURE_TEST_CASE(SM_emptyChain, SM_CryptoTestFixture)
{
    // special case
    EmptyFixture empty;

    BOOST_CHECK_EQUAL(empty.m_blockChainImp->number(), 0);
    BOOST_CHECK_EQUAL(empty.m_blockChainImp->totalTransactionCount().first, 0);
    BOOST_CHECK_EQUAL(empty.m_blockChainImp->totalTransactionCount().second, 0);
    BOOST_CHECK_NO_THROW(empty.m_blockChainImp->getCode(Address(0x0)));

    BOOST_CHECK_EQUAL(empty.m_blockChainImp->numberHash(0),
        h256("39b4e98c07189c1a1cc53d8159b294c6b58753e660aa52d3a25c5241cc6225f9"));

    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getBlockByHash(h256(c_commonHashPrefix)), std::shared_ptr<Block>());
    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getLocalisedTxByHash(h256(c_commonHashPrefix))->blockHash(),
        h256(0));
    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getLocalisedTxByHash(h256(c_commonHashPrefix))->blockNumber(), -1);
    BOOST_CHECK_EQUAL(
        *(empty.m_blockChainImp->getTxByHash(h256(c_commonHashPrefix))), Transaction());
    BOOST_CHECK_EQUAL(
        crypto::Hash(
            empty.m_blockChainImp->getTransactionReceiptByHash(h256(c_commonHashPrefix))->rlp()),
        crypto::Hash(TransactionReceipt().rlp()));
    BOOST_CHECK_EQUAL(
        empty.m_blockChainImp->getLocalisedTxReceiptByHash(h256(c_commonHashPrefix))->hash(),
        h256(0));
}

BOOST_AUTO_TEST_SUITE_END()

}  // namespace test
}  // namespace dev

max_stars_repo_name: imtypist/FISCO-BCOS

max_issues_repo_name: imtypist/FISCO-BCOS

max_forks_repo_name: imtypist/FISCO-BCOS