text:
/*
 * ExerciseManagement.cpp
 *
 *  Created on: 2018年11月12日
 *      Author: peter
 */

#include "ExerciseManagement.hpp"
#include "SARecorder.hpp"

#include <string>
#include <unordered_map>
#include <boost/lexical_cast.hpp>

#ifndef MYSQLPP_MYSQL_HEADERS_BURIED
#	define MYSQLPP_MYSQL_HEADERS_BURIED
#endif

#include <mysql++/query.h>
#include <mysql++/connection.h>

using namespace std::string_literals;

void ExerciseManagement::refresh_all_users_submit_and_accept_num(mysqlpp::Connection & mysql_conn)
{
	std::unordered_map<ojv4::u_id_type, UserSARecorder, ojv4::u_id_type::hash> m;
	{
		mysqlpp::Query query = mysql_conn.query(
				"select u_id, p_id, s_result from solution "
				"order by s_id"
				// order by s_id 是重中之重, 因为根据 update 先后次序的不同, s_id 小的可能在 s_id 大的 solution 后面
		);

		// 性能优化提示 1: 这边用 Query::use(), 比使用 store() 方法先把数据保存下来然后再遍历效率要更高
		mysqlpp::UseQueryResult solutions = query.use();
		if (!solutions) {
			throw std::runtime_error("Query users' solutions in exercise failed!"s
					+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
					+ " MySQL error: " + mysql_conn.error());
		}

		while (::MYSQL_ROW row = solutions.fetch_raw_row()) {
			/*
			 * 性能优化提示 2:
			 * 使用 C 风格版裸的 MYSQL_ROW 类型比 C++ 风格的 mysql::row 类型效率要高,
			 * 因为前者就是 char**，而后者是对 vector<string> 的封装
			 * 当然改用 MYSQL_ROW 的代价是少了类型检查、越界检查等, 代码不安全.
			 * 当然该函数工作量较大, 这里是为了性能考虑, 不得不做妥协
			 *
			 * 性能优化提示 3:
			 * 使用 boost::lexical_cast 而不使用 stox 的理由是:
			 * stox 的参数为 const std::string&, 会从 const char* 构造一个 string 类型临时变量, 效率低;
			 * 而 boost::lexical_cast 支持从 const char* 转换
			 * 不使用 C 语言风格的 atox 系列函数的理由是: atox 没有异常机制, 如若传进去的字符串非法就可能有隐患
			 * 尽管 boost::lexical_cast 的效率略低于 atox, 但在满足安全原则的前提下低不了多少
			 */
			auto u_id(boost::lexical_cast<ojv4::u_id_type>(row[0]));
			auto p_id(boost::lexical_cast<ojv4::p_id_type>(row[1]));
			auto s_result(ojv4::s_result_enum(boost::lexical_cast<int>(row[2])));
			m[u_id].add_solution(p_id, s_result);
		}
	}

	mysqlpp::Transaction trans(mysql_conn);

	// 刷新前，先把所有用户的 SA 数置零
	if (!mysql_conn.query("update user set u_submit = 0, u_accept = 0").execute()) {
		throw std::runtime_error("Clear users' submit and accept num in exercise failed!"s
				+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
				+ " MySQL error: " + mysql_conn.error());
	}

	mysqlpp::Query update = mysql_conn.query(
			"update user set u_submit = %1, u_accept = %2 "
			"where u_id = %0"
	);
	update.parse();

	for (const auto & [u_id, u_sa_info] : m) {
		if (!update.execute(u_id, u_sa_info.submit_num(), u_sa_info.accept_num())) {
			throw std::runtime_error("Update user's submit and accept num in exercise failed!"s
					+ " u_id = " + std::to_string(u_id)
					+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
					+ " MySQL error: " + mysql_conn.error());
		}
	}
	trans.commit();
}

void ExerciseManagement::refresh_all_problems_submit_and_accept_num(mysqlpp::Connection & mysql_conn)
{
	std::unordered_map<ojv4::p_id_type, ProblemSARecorder, ojv4::p_id_type::hash> m;
	{
		mysqlpp::Query query = mysql_conn.query(
				"select u_id, p_id, s_result from solution "
				"order by s_id"
		// order by s_id 是重中之重, 因为根据 update 先后次序的不同, s_id 小的可能在 s_id 大的 solution 后面
		);

		mysqlpp::UseQueryResult solutions = query.use();
		if (!solutions) {
			throw std::runtime_error("Query problems' solutions in exercise failed!"s
					+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
					+ " MySQL error: " + mysql_conn.error());
		}

		while (::MYSQL_ROW row = solutions.fetch_raw_row()) {
			auto u_id(boost::lexical_cast<ojv4::u_id_type>(row[0]));
			auto p_id(boost::lexical_cast<ojv4::p_id_type>(row[1]));
			auto s_result(ojv4::s_result_enum(boost::lexical_cast<int>(row[2])));
			m[p_id].add_solution(u_id, s_result);
		}
	}

	mysqlpp::Transaction trans(mysql_conn);

	if (!mysql_conn.query("update problem set p_submit = 0, p_accept = 0").execute()) {
		throw std::runtime_error("Clear problems' submit and accept num in exercise failed!"s
				+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
				+ " MySQL error: " + mysql_conn.error());
	}

	mysqlpp::Query update = mysql_conn.query(
			"update problem set p_submit = %1, p_accept = %2 "
			"where p_id = %0"
	);
	update.parse();

	for (const auto & [p_id, p_sa_info] : m) {
		if (!update.execute(p_id, p_sa_info.submit_num(), p_sa_info.accept_num())) {
			throw std::runtime_error("Update problem's submit and accept num in exercise failed!"s
					+ " p_id = " + std::to_string(p_id)
					+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
					+ " MySQL error: " + mysql_conn.error());
		}
	}
	trans.commit();
}

void ExerciseManagement::refresh_all_user_problem(mysqlpp::Connection & mysql_conn)
{
	struct pair_hash
	{
			size_t operator()(const std::pair<ojv4::u_id_type, ojv4::p_id_type> & p) const
			{
//				return id_type_hash<ojv4::u_id_type>()(p.first) ^ id_type_hash<ojv4::p_id_type>()(p.second);

				size_t hash = ojv4::u_id_type::hash()(p.first);
				hash = hash * 48271UL + ojv4::p_id_type::hash()(p.second);
				return hash;
			}
	};

	struct tuple_hash
	{
			size_t operator()(const std::tuple<ojv4::u_id_type, ojv4::p_id_type, ojv4::c_id_type> & p) const
			{
//				return
//				 id_type_hash<ojv4::u_id_type>()(std::get<0>(p)) +
//				 id_type_hash<ojv4::p_id_type>()(std::get<1>(p)) +
//				 id_type_hash<ojv4::c_id_type>()(std::get<2>(p));

				/*
				 * 一个适用于 pair, tuple 的低冲突概率哈希策略的灵感来源:
				 * 由于 STL 没有为 pair 及 tuple 提供默认的哈希实现, 因此如果想使用 pair 或 tuple
				 * 做为 unordered_map 的键类型的话, 就需要我们自己实现一个哈希策略.
				 * 在初步编码时, 为了方便, 笔者选择了最简单的方案, 即: hash(tuple) = hash(tuple[0]) + hash(tuple[1]) + hash(tuple[2])
				 * 在优化此方法时, 笔者使用 MySQL 中的全真数据做了一下测试 (当时 max(s_id) = 214601),
				 * 发现部分哈希值碰撞次数达到 20 - 30 甚至 50 之多. 而我们知道, 为了提高效率, 哈希冲突显然是越小越好
				 * 于是第二版的哈希策略我借鉴了标准库 <random> 中 minstd_rand 的实现原理
				 *
				 * typedef linear_congruential_engine<uint_fast32_t, 16807UL, 0UL, 2147483647UL>
				 * minstd_rand0;
				 * typedef linear_congruential_engine<uint_fast32_t, 48271UL, 0UL, 2147483647UL>
				 * minstd_rand;
				 *
				 * minstd_rand 是一个代入了特化参数的线性同余随机数引擎, linear_congruential_engine 的数学本质就是,
				 * 在上一次产生的随机数的基础上, 乘以一个较大的素数, 再加上一个偏移量, 最后对所设定的最大上限求余, 得到下一个随机数.
				 * 这样, 就以较为简单的方式, 产生了一系列分布较为均匀的伪随机数. 而这里的 "分布均匀" 不就是我们所追求的 "低冲突" 吗?
				 * 最后, 笔者用优化过的哈希策略使用同样的数据进行测试, 发现竟一次冲突也没有发生!
				 * 同时, 两个 map 的计算时间也由 700 ms 降低到了 600 ms
				 */
				size_t hash = ojv4::u_id_type::hash()(std::get<0>(p));
				hash = hash * 48271UL + ojv4::p_id_type::hash()(std::get<1>(p));
				hash = hash * 48271UL + ojv4::c_id_type::hash()(std::get<2>(p));
				return hash;
			}
	};

	std::unordered_map<std::pair<ojv4::u_id_type, ojv4::p_id_type>, ojv4::u_p_status_enum, pair_hash> u_p_status;
	std::unordered_map<std::tuple<ojv4::u_id_type, ojv4::p_id_type, ojv4::c_id_type>, ojv4::u_p_status_enum, tuple_hash> u_p_status_of_course;
	{
		mysqlpp::Query query = mysql_conn.query(
				"select u_id, p_id, ( "
				"	case "
				"	when c_id is NULL then NULL " // 原本就是练习中的提交, 不做任何处理
				"	when ( u_id in (select u_id from course_user where c_id = solution.c_id) and "
				"		   p_id in (select p_id from course_problem where c_id = solution.c_id) "
				"	) then c_id " // 人/题还在课程中, 则使用原 c_id
				"	else NULL " // 如果是被删过的人/题, 则仍被看作是练习中的一次提交
				"	end "
				") as c_id_considering_delete, s_result "
				"from solution order by s_id"
		);
		/*
		 * c_id_considering_delete 的含义:
		 * 基于之前的文档我们知道, 如果课程里删除过人或者题以后, 那么计算提交数通过数时需要过滤掉已删除的人/题;
		 * 同样的, 在重算 user-problem 时也需要考虑到这个问题.
		 * 我们知道, 在课程中提交了一次会被视为也在练习中提交了一次处理的.
		 * 所以, 如果课程中删人了, 那我们在此约定, 受到影响的 solution 不再算作是课程中的提交, 但是仍算作是练习中的一次提交
		 */
		mysqlpp::UseQueryResult solutions = query.use();

		if (!solutions) {
			throw std::runtime_error("Query solutions failed!"s
					+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
					+ " MySQL error: " + mysql_conn.error());
		}

		while (::MYSQL_ROW row = solutions.fetch_raw_row()) {
			auto u_id(boost::lexical_cast<ojv4::u_id_type>(row[0]));
			auto p_id(boost::lexical_cast<ojv4::p_id_type>(row[1]));
			auto s_result(ojv4::s_result_enum(boost::lexical_cast<int>(row[3])));
			switch (s_result) {
				case ojv4::s_result_enum::ACCEPTED:
					u_p_status[ { u_id, p_id }] = ojv4::u_p_status_enum::ACCEPTED;
					if (row[2] != nullptr) {
						auto c_id(boost::lexical_cast<ojv4::c_id_type>(row[2]));
						u_p_status_of_course[ { u_id, p_id, c_id }] = ojv4::u_p_status_enum::ACCEPTED;
					}
					break;
				case ojv4::s_result_enum::SYSTEM_ERROR:
					break;
				default:
					//inserts if the key does not exist, does nothing if the key exists
					u_p_status.insert( { { u_id, p_id }, ojv4::u_p_status_enum::ATTEMPTED });
					if (row[2] != nullptr) {
						auto c_id(boost::lexical_cast<ojv4::c_id_type>(row[2]));
						u_p_status_of_course.insert( { { u_id, p_id, c_id }, ojv4::u_p_status_enum::ATTEMPTED });
					}
			}
		}
	}

	mysqlpp::Transaction trans(mysql_conn);

	if (!(mysql_conn.query("delete from user_problem").execute())) {
		throw std::runtime_error("Clear user_problem table failed!"s
				+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
				+ " MySQL error: " + mysql_conn.error());
	}

	{
		mysqlpp::Query insert = mysql_conn.query(
				"insert into user_problem(u_id, p_id, status, c_id) "
				"values("
		);
		auto begin = u_p_status.cbegin();
		auto end = u_p_status.cend();

		if (begin != end) {
			const auto & [key, status] = *begin;
			const auto & [u_id, p_id] = key;
			insert << u_id << ',' << p_id << ',' << int(status) << ",null)";

			while (++begin != end) {
				const auto & [key, status] = *begin;
				const auto & [u_id, p_id] = key;
				insert << ",(" << u_id << ',' << p_id << ',' << int(status) << ",null)";
			}
			if (!insert.execute()) {
				throw std::runtime_error("Update user_problem table failed!"s
						+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
						+ " MySQL error: " + mysql_conn.error());
			}
		}
	}

	{
		mysqlpp::Query insert = mysql_conn.query(
				"insert into user_problem(u_id, p_id, status, c_id) "
				"values("
		);
		auto begin = u_p_status_of_course.cbegin();
		auto end = u_p_status_of_course.cend();

		if (begin != end) {
			const auto & [key, status] = *begin;
			const auto & [u_id, p_id, c_id] = key;
			insert << u_id << ',' << p_id << ',' << int(status) << ',' << c_id << ')';

			while (++begin != end) {
				const auto & [key, status] = *begin;
				const auto & [u_id, p_id, c_id] = key;
				insert << ",(" << u_id << ',' << p_id << ',' << int(status) << ',' << c_id << ')';
			}
			if (!insert.execute()) {
				throw std::runtime_error("Update user_problem table failed!"s
						+ " MySQL errnum: " + std::to_string(mysql_conn.errnum())
						+ " MySQL error: " + mysql_conn.error());
			}
		}
	}

	trans.commit();
}

void ExerciseManagement::update_user_s_submit_and_accept_num(mysqlpp::Connection & conn, ojv4::u_id_type u_id)
{
	UserSARecorder u_info;
	{
		mysqlpp::Query query = conn.query(
				"select p_id, s_result from solution "
				"where u_id = %0 order by s_id"
				// order by s_id 是重中之重, 因为根据 update 先后次序的不同, s_id 小的可能在 s_id 大的 solution 后面
		);
		query.parse();

		mysqlpp::UseQueryResult solutions = query.use(u_id);
		if (!solutions) {
			throw std::runtime_error("Query user's solutions failed!"s
					+ " u_id = " + std::to_string(u_id)
					+ " MySQL errnum: " + std::to_string(conn.errnum())
					+ " MySQL error: " + conn.error());
		}

		while (::MYSQL_ROW row = solutions.fetch_raw_row()) {
			auto p_id(boost::lexical_cast<ojv4::p_id_type>(row[0]));
			auto s_result(ojv4::s_result_enum(boost::lexical_cast<int>(row[1])));
			u_info.add_solution(p_id, s_result);
		}
	}

	mysqlpp::Query update = conn.query(
			"update user set u_submit = %1, u_accept = %2 "
			"where u_id = %0"
	);
	update.parse();
	if (!update.execute(u_id, u_info.submit_num(), u_info.accept_num())) {
		throw std::runtime_error("Update user's submit and accept num failed!"s
				+ " u_id = " + std::to_string(u_id)
				+ " MySQL errnum: " + std::to_string(conn.errnum())
				+ " MySQL error: " + conn.error());
	}
}

void ExerciseManagement::update_problem_s_submit_and_accept_num(mysqlpp::Connection & conn, ojv4::p_id_type p_id)
{
	ProblemSARecorder p_info;
	{
		mysqlpp::Query query = conn.query(
				"select u_id, s_result from solution "
				"where p_id = %0 order by s_id"
				// order by s_id 是重中之重, 因为根据 update 先后次序的不同, s_id 小的可能在 s_id 大的 solution 后面
		);
		query.parse();

		mysqlpp::UseQueryResult solutions = query.use(p_id);
		if (!solutions) {
			throw std::runtime_error("Query problem's solutions failed!"s
					+ " p_id = " + std::to_string(p_id)
					+ " MySQL errnum: " + std::to_string(conn.errnum())
					+ " MySQL error: " + conn.error());
		}

		while (::MYSQL_ROW row = solutions.fetch_raw_row()) {
			auto u_id(boost::lexical_cast<ojv4::u_id_type>(row[0]));
			auto s_result(ojv4::s_result_enum(boost::lexical_cast<int>(row[1])));
			p_info.add_solution(u_id, s_result);
		}
	}

	mysqlpp::Query update = conn.query(
			"update problem set p_submit = %1, p_accept = %2 "
			"where p_id = %0"
	);
	update.parse();
	if (!update.execute(p_id, p_info.submit_num(), p_info.accept_num())) {
		throw std::runtime_error("Update problem's submit and accept num failed!"s
				+ " p_id = " + std::to_string(p_id)
				+ " MySQL errnum: " + std::to_string(conn.errnum())
				+ " MySQL error: " + conn.error());
	}
}

ojv4::u_p_status_enum query_user_s_problem_status_from_solution(mysqlpp::Connection & conn, ojv4::u_id_type u_id, ojv4::p_id_type p_id)
{
	constexpr ojv4::s_result_in_db_type ACCEPTED = ojv4::s_result_in_db_type(ojv4::s_result_enum::ACCEPTED);
	constexpr ojv4::s_result_in_db_type SYSTEM_ERROR = ojv4::s_result_in_db_type(ojv4::s_result_enum::SYSTEM_ERROR);

	mysqlpp::Query query_status = conn.query(
			"select ("
			"	select exists(select s_id from solution where u_id = %0 and p_id = %1 and s_result <> %3)"
			") as has_submit, ("
			"	select exists(select s_id from solution where u_id = %0 and p_id = %1 and s_result = %2)"
			") as has_accepted"
	); // 不要加 c_id is null 的判断！！！ 用户在课程中提交一题, 在练习中看到的状态也是 AC 的
	query_status.parse();

	mysqlpp::StoreQueryResult res = query_status.store(u_id, p_id, ACCEPTED, SYSTEM_ERROR);

	if (bool(res[0]["has_accepted"]) == true) {
		return ojv4::u_p_status_enum::ACCEPTED;
	} else if (bool(res[0]["has_submit"]) == true) {
		return ojv4::u_p_status_enum::ATTEMPTED;
	} else {
		return ojv4::u_p_status_enum::TODO;
	}
}

void ExerciseManagement::update_user_problem_status(mysqlpp::Connection & conn, ojv4::u_id_type u_id, ojv4::p_id_type p_id)
{
}

void ExerciseManagement::update_user_problem(mysqlpp::Connection & conn, ojv4::u_id_type u_id, ojv4::p_id_type p_id)
{
	ojv4::u_p_status_enum new_status = ojv4::u_p_status_enum::ATTEMPTED;

	try {
		new_status = query_user_s_problem_status_from_solution(conn, u_id, p_id);
	} catch (const std::exception & e) {
		throw std::runtime_error("Query user's problem status failed!"s
				+ " u_id = " + std::to_string(u_id)
				+ " p_id = " + std::to_string(p_id)
				+ " details: " + e.what());
	}

	if (new_status == ojv4::u_p_status_enum::TODO) {
		return;
	}

	ojv4::u_p_status_enum previous_status = ojv4::u_p_status_enum::TODO;
	{
		mysqlpp::Query query = conn.query(
				"select status from user_problem "
				"where u_id = %0 and p_id = %1 and c_id is null");
		query.parse();

		mysqlpp::StoreQueryResult res = query.store(u_id, p_id);
		if (!res) {
			throw std::runtime_error("Query previous status from user-problem failed!"s
					+ " u_id = " + std::to_string(u_id)
					+ " p_id = " + std::to_string(p_id)
					+ " MySQL errnum: " + std::to_string(conn.errnum())
					+ " MySQL error: " + conn.error());
		}
		if (res.empty()) {
			previous_status = ojv4::u_p_status_enum::TODO;
		} else {
			previous_status = ojv4::u_p_status_enum(int(res[0]["status"]));
		}
	}

	if (previous_status == ojv4::u_p_status_enum::TODO) {
		mysqlpp::Query insert = conn.query(
				"insert into user_problem (u_id, p_id, status, c_id) "
				"values (%0, %1, %2, null)"
		);
		insert.parse();
        if (!insert.execute(u_id, p_id, int(new_status))) {
            throw std::runtime_error("Insert user-problem failed!"s
            		+ " u_id = " + std::to_string(u_id)
            		+ " p_id = " + std::to_string(p_id)
             		+ " MySQL errnum: " + std::to_string(conn.errnum())
             		+ " MySQL error: " + conn.error());
        }
	} else if (previous_status != new_status) {
		mysqlpp::Query update = conn.query(
				"update user_problem set "
				"status = %0 "
				"where u_id = %1 and p_id = %2 and c_id is null"
		);
		update.parse();
        if (!update.execute(int(new_status), u_id, p_id)) {
            throw std::runtime_error("Update user-problem failed!"s
             		+ " u_id = " + std::to_string(u_id)
             		+ " p_id = " + std::to_string(p_id)
             		+ " MySQL errnum: " + std::to_string(conn.errnum())
             		+ " MySQL error: " + conn.error());
        }
	}

}

ext: cpp


text:
#include <ripple/basics/make_SSLContext.h>
#include <ripple/beast/rfc2616.h>
#include <ripple/server/Server.h>
#include <ripple/server/Session.h>
#include <ripple/beast/unit_test.h>
#include <ripple/core/ConfigSections.h>
#include <test/jtx.h>
#include <test/jtx/envconfig.h>
#include <test/unit_test/SuiteJournal.h>
#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <chrono>
#include <stdexcept>
#include <thread>

namespace ripple {
namespace test {

class Server_test : public beast::unit_test::suite
{
public:

    class TestThread
    {
    private:
        boost::asio::io_service io_service_;
        boost::optional<boost::asio::io_service::work> work_;
        std::thread thread_;

    public:
        TestThread()
            : work_(boost::in_place(std::ref(io_service_)))
            , thread_([&]() { this->io_service_.run(); })
        {
        }

        ~TestThread()
        {
            work_ = boost::none;
            thread_.join();
        }

        boost::asio::io_service&
        get_io_service()
        {
            return io_service_;
        }
    };

    class TestSink : public beast::Journal::Sink
    {
        beast::unit_test::suite& suite_;

    public:
        explicit TestSink (beast::unit_test::suite& suite)
            : Sink (beast::severities::kWarning, false)
            , suite_ (suite)
        {
        }

        void
        write (beast::severities::Severity level,
            std::string const& text) override
        {
            if (level < threshold())
                return;

            suite_.log << text << std::endl;
        }
    };

    struct TestHandler
    {
        bool
        onAccept (Session& session,
            boost::asio::ip::tcp::endpoint endpoint)
        {
            return true;
        }

        Handoff
        onHandoff (Session& session,
            std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
                http_request_type&& request,
                    boost::asio::ip::tcp::endpoint remote_address)
        {
            return Handoff{};
        }

        Handoff
        onHandoff (Session& session, http_request_type&& request,
            boost::asio::ip::tcp::endpoint remote_address)
        {
            return Handoff{};
        }

        void
        onRequest (Session& session)
        {
            session.write (std::string ("Hello, world!\n"));
            if (beast::rfc2616::is_keep_alive(session.request()))
                session.complete();
            else
                session.close (true);
        }

        void
        onWSMessage(std::shared_ptr<WSSession> session,
            std::vector<boost::asio::const_buffer> const&)
        {
        }

        void
        onClose (Session& session,
            boost::system::error_code const&)
        {
        }

        void
        onStopped (Server& server)
        {
        }
    };

    template <class Socket>
    bool
    connect (Socket& s, typename Socket::endpoint_type const& ep)
    {
        try
        {
            s.connect (ep);
            pass();
            return true;
        }
        catch (std::exception const& e)
        {
            fail (e.what());
        }

        return false;
    }

    template <class SyncWriteStream>
    bool
    write (SyncWriteStream& s, std::string const& text)
    {
        try
        {
            boost::asio::write (s, boost::asio::buffer (text));
            pass();
            return true;
        }
        catch (std::exception const& e)
        {
            fail (e.what());
        }
        return false;
    }

    template <class SyncReadStream>
    bool
    expect_read (SyncReadStream& s, std::string const& match)
    {
        boost::asio::streambuf b (1000);
        try
        {
            auto const n = boost::asio::read_until (s, b, '\n');
            if (BEAST_EXPECT(n == match.size()))
            {
                std::string got;
                got.resize (n);
                boost::asio::buffer_copy (boost::asio::buffer (
                    &got[0], n), b.data());
                return BEAST_EXPECT(got == match);
            }
        }
        catch (std::length_error const& e)
        {
            fail(e.what());
        }
        catch (std::exception const& e)
        {
            fail(e.what());
        }
        return false;
    }

    void
    test_request(boost::asio::ip::tcp::endpoint const& ep)
    {
        boost::asio::io_service ios;
        using socket = boost::asio::ip::tcp::socket;
        socket s (ios);

        if (! connect (s, ep))
            return;

        if (! write (s,
            "GET / HTTP/1.1\r\n"
            "Connection: close\r\n"
            "\r\n"))
            return;

        if (! expect_read (s, "Hello, world!\n"))
            return ;

        boost::system::error_code ec;
        s.shutdown(socket::shutdown_both, ec);

        std::this_thread::sleep_for (std::chrono::seconds (1));
    }

    void
    test_keepalive(boost::asio::ip::tcp::endpoint const& ep)
    {
        boost::asio::io_service ios;
        using socket = boost::asio::ip::tcp::socket;
        socket s (ios);

        if (! connect (s, ep))
            return;

        if (! write (s,
            "GET / HTTP/1.1\r\n"
            "Connection: Keep-Alive\r\n"
            "\r\n"))
            return;

        if (! expect_read (s, "Hello, world!\n"))
            return ;

        if (! write (s,
            "GET / HTTP/1.1\r\n"
            "Connection: close\r\n"
            "\r\n"))
            return;

        if (! expect_read (s, "Hello, world!\n"))
            return ;

        boost::system::error_code ec;
        s.shutdown(socket::shutdown_both, ec);
    }

    void basicTests()
    {
        testcase("Basic client/server");
        TestSink sink {*this};
        TestThread thread;
        sink.threshold (beast::severities::Severity::kAll);
        beast::Journal journal {sink};
        TestHandler handler;
        auto s = make_Server (handler,
            thread.get_io_service(), journal);
        std::vector<Port> serverPort(1);
        serverPort.back().ip =
            beast::IP::Address::from_string (getEnvLocalhostAddr()),
        serverPort.back().port = 0;
        serverPort.back().protocol.insert("http");
        auto eps = s->ports (serverPort);
        log << "server listening on port " << eps[0].port() << std::endl;
        test_request(eps[0]);
        test_keepalive(eps[0]);
        s = nullptr;
        pass();
    }

    void stressTest()
    {
        testcase("stress test");
        struct NullHandler
        {
            bool
            onAccept (Session& session,
                boost::asio::ip::tcp::endpoint endpoint)
            {
                return true;
            }

            Handoff
            onHandoff (Session& session,
                std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
                    http_request_type&& request,
                        boost::asio::ip::tcp::endpoint remote_address)
            {
                return Handoff{};
            }

            Handoff
            onHandoff (Session& session, http_request_type&& request,
                boost::asio::ip::tcp::endpoint remote_address)
            {
                return Handoff{};
            }

            void
            onRequest (Session& session)
            {
            }

            void
            onWSMessage(std::shared_ptr<WSSession> session,
                std::vector<boost::asio::const_buffer> const& buffers)
            {
            }

            void
            onClose (Session& session,
                boost::system::error_code const&)
            {
            }

            void
            onStopped (Server& server)
            {
            }
        };

        using namespace beast::severities;
        SuiteJournal journal ("Server_test", *this);

        NullHandler h;
        for(int i = 0; i < 1000; ++i)
        {
            TestThread thread;
            auto s = make_Server(h,
                thread.get_io_service(), journal);
            std::vector<Port> serverPort(1);
            serverPort.back().ip =
                beast::IP::Address::from_string (getEnvLocalhostAddr()),
            serverPort.back().port = 0;
            serverPort.back().protocol.insert("http");
            s->ports (serverPort);
        }
        pass();
    }

    class CaptureSink : public beast::Journal::Sink
    {
        std::stringstream& strm_;
    public:
        CaptureSink(beast::severities::Severity threshold,
            std::stringstream& strm)
        : beast::Journal::Sink(threshold, false)
        , strm_(strm)
        {
        }

        void
        write(beast::severities::Severity level, std::string const& text) override
        {
            strm_ << text;
        }
    };

    class CaptureLogs : public Logs
    {
        std::stringstream strm_;
        std::string& result_;

    public:
        explicit CaptureLogs(std::string& result)
            : Logs (beast::severities::kInfo)
            , result_(result)
        {
        }

        ~CaptureLogs() override
        {
            result_ = strm_.str();
        }

        std::unique_ptr<beast::Journal::Sink>
        makeSink(std::string const& partition,
            beast::severities::Severity threshold) override
        {
            return std::make_unique<CaptureSink>(threshold, strm_);
        }
    };

    void
    testBadConfig ()
    {
        testcase ("Server config - invalid options");
        using namespace test::jtx;

        std::string messages;

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    (*cfg).deprecatedClearSection("port_rpc");
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Missing 'ip' in [port_rpc]")
            != std::string::npos);

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    (*cfg).deprecatedClearSection("port_rpc");
                    (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Missing 'port' in [port_rpc]")
            != std::string::npos);

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    (*cfg).deprecatedClearSection("port_rpc");
                    (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
                    (*cfg)["port_rpc"].set("port", "0");
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Invalid value '0' for key 'port' in [port_rpc]")
            != std::string::npos);

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    (*cfg).deprecatedClearSection("port_rpc");
                    (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
                    (*cfg)["port_rpc"].set("port", "8081");
                    (*cfg)["port_rpc"].set("protocol", "");
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Missing 'protocol' in [port_rpc]")
            != std::string::npos);

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    cfg = std::make_unique<Config>();
                    cfg->overwrite (
                        ConfigSection::nodeDatabase (), "type", "memory");
                    cfg->overwrite (
                        ConfigSection::nodeDatabase (), "path", "main");
                    cfg->deprecatedClearSection (
                        ConfigSection::importNodeDatabase ());
                    cfg->legacy("database_path", "");
                    cfg->setupControl(true, true, true);
                    (*cfg)["port_peer"].set("ip", getEnvLocalhostAddr());
                    (*cfg)["port_peer"].set("port", "8080");
                    (*cfg)["port_peer"].set("protocol", "peer");
                    (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
                    (*cfg)["port_rpc"].set("port", "8081");
                    (*cfg)["port_rpc"].set("protocol", "http,ws2");
                    (*cfg)["port_rpc"].set("admin", getEnvLocalhostAddr());
                    (*cfg)["port_ws"].set("ip", getEnvLocalhostAddr());
                    (*cfg)["port_ws"].set("port", "8082");
                    (*cfg)["port_ws"].set("protocol", "ws");
                    (*cfg)["port_ws"].set("admin", getEnvLocalhostAddr());
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Required section [server] is missing")
            != std::string::npos);

        except ([&]
        {
            Env env {*this,
                envconfig([](std::unique_ptr<Config> cfg) {
                    cfg = std::make_unique<Config>();
                    cfg->overwrite (ConfigSection::nodeDatabase (), "type", "memory");
                    cfg->overwrite (ConfigSection::nodeDatabase (), "path", "main");
                    cfg->deprecatedClearSection (ConfigSection::importNodeDatabase ());
                    cfg->legacy("database_path", "");
                    cfg->setupControl(true, true, true);
                    (*cfg)["server"].append("port_peer");
                    (*cfg)["server"].append("port_rpc");
                    (*cfg)["server"].append("port_ws");
                    return cfg;
                }),
                std::make_unique<CaptureLogs>(messages)};
        });
        BEAST_EXPECT (
            messages.find ("Missing section: [port_peer]")
            != std::string::npos);
    }

    void
    run() override
    {
        basicTests();
        stressTest();
        testBadConfig();
    }
};

BEAST_DEFINE_TESTSUITE(Server,http,ripple);

}
}


text:
//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2::linspace function"

#include <nt2/table.hpp>
#include <nt2/include/functions/linspace.hpp>
#include <nt2/include/functions/predecessor.hpp>
#include <nt2/include/functions/globalall.hpp>
#include <nt2/include/functions/of_size.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <boost/dispatch/meta/nth_hierarchy.hpp>

NT2_TEST_CASE( hierarchy )
{
  using boost::mpl::_;
  using boost::mpl::int_;
  using boost::dispatch::meta::nth_hierarchy;

  NT2_TEST_EXPR_TYPE( (nt2::tag::linspace_() )
                    , (nth_hierarchy<_,int_<0> >)
                    , (nt2::tag::linspace_)
                    );

  NT2_TEST_EXPR_TYPE( (nt2::tag::linspace_() )
                    , (nth_hierarchy<_,int_<1> >)
                    , (nt2::ext::state_constant_< nt2::tag::linspace_ >
                      )
                    );

  NT2_TEST_EXPR_TYPE( (nt2::tag::linspace_() )
                    , (nth_hierarchy<_,int_<2> >)
                    , (nt2::ext::constant_< nt2::tag::linspace_ >
                      )
                    );

  NT2_TEST_EXPR_TYPE( (nt2::tag::linspace_() )
                    , (nth_hierarchy<_,int_<3> >)
                    , (nt2::ext::elementwise_<nt2::tag::linspace_>)
                    );

  NT2_TEST_EXPR_TYPE( (nt2::tag::linspace_() )
                    , (nth_hierarchy<_,int_<4> >)
                    , (nt2::ext::unspecified_<nt2::tag::linspace_>)
                    );
}

NT2_TEST_CASE_TPL( linspace, NT2_REAL_TYPES )
{
  using nt2::meta::value_type_;

  nt2::table<T> ref(nt2::of_size(1,100));
  for(int i=1;i<=100;++i)
    ref(i) = T(0) + (T(1)/99)*(i-1);

  NT2_TEST_EXPR_TYPE( (nt2::linspace(T(0),T(1)))
                    , (value_type_<boost::mpl::_>)
                    , (T)
                    );

  nt2::table<T> x = nt2::linspace(T(0),T(1));
  NT2_TEST_EQUAL( x.extent(), nt2::of_size(1,100) );
  NT2_TEST_ULP_EQUAL( x, ref, 15 );

  for(int i=1;i<=100;++i)
    ref(i) = T(1) + (T(-1)/99)*(i-1);

  nt2::table<T> xr = nt2::linspace(T(1),T(0));
  NT2_TEST_EQUAL( xr.extent(), nt2::of_size(1,100) );
  NT2_TEST_ULP_EQUAL( xr, ref, 15 );
}

NT2_TEST_CASE_TPL( linspace_with_size, NT2_REAL_TYPES )
{
  using nt2::meta::value_type_;

  nt2::table<T> ref(nt2::of_size(1,7));
  for(int i=1;i<=7;++i)
    ref(i) = T(0) + (T(1)/6)*(i-1);

  NT2_TEST_EXPR_TYPE( (nt2::linspace(T(0),T(1),7))
                    , (value_type_<boost::mpl::_>)
                    , (T)
                    );

  nt2::table<T> x = nt2::linspace(T(0),T(1),7);
  NT2_TEST_EQUAL( x.extent(), nt2::of_size(1,7) );
  NT2_TEST_ULP_EQUAL( x, ref, 1 );

  for(int i=1;i<=7;++i)
    ref(i) = T(1) + (T(-1)/6)*(i-1);

  nt2::table<T> xr = nt2::linspace(T(1),T(0),7);
  NT2_TEST_EQUAL( xr.extent(), nt2::of_size(1,7) );
  NT2_TEST_ULP_EQUAL( xr, ref, 1 );

  nt2::table<T> xn1 = nt2::linspace(T(0),T(9),1);
  NT2_TEST_EQUAL( xn1.extent(), nt2::of_size(1,1 ) );
  NT2_TEST_EQUAL( xn1(1), 9 );
}

NT2_TEST_CASE_TPL( linspace_worst, NT2_REAL_TYPES )
{
  for(size_t i = 0; i < 8; ++i)
  {
    nt2::table<T> z = nt2::linspace(T(-100), nt2::predecessor(T(0)), 100+i);
    NT2_TEST(nt2::globalall(z));
    z(nt2::end_) = T(nt2::predecessor(T(0)));
    NT2_TEST(nt2::globalall(z));
  }
}

NT2_TEST_CASE_TPL( linspace_best, NT2_REAL_TYPES )
{
  nt2::table<T> z = nt2::linspace(T(-1), T(1), 257);
  nt2::table<T> ref(nt2::of_size(1,257));

  for(size_t i = 1; i <= 257; ++i)
    ref(i) =  T(-1)+T(i-1)/128;

  NT2_TEST_EQUAL(z,ref);
}
NT2_TEST_CASE_TPL( linspace_cplx, (double)(float) )
{
  typedef std::complex<T> cT;
  nt2::table<cT> z = nt2::linspace(cT(-1, -1), cT(1, 1), 257);
  nt2::table<cT> ref(nt2::of_size(1,257));

  for(size_t i = 1; i <= 257; ++i)
    ref(i) =  cT(-1, -1)+cT(T(i-1)/128, T(i-1)/128);

  NT2_TEST_EQUAL(z,ref);
}


text:
#include <iostream>
#include <fstream>

#include "XercesUtils.hpp"
#include "LoadOSM.hpp"

#include "OSMDatabase.hpp"
#include "BasicRelationFeatureFactory.hpp"
#include "BasicWayFeatureFactory.hpp"

#include <boost/archive/binary_oarchive.hpp>

#include <cairomm/context.h>
#include <cairomm/surface.h>

#include <array>

using namespace std;

#include "MultipolyCloser.hpp"

const std::vector<std::array<float,3>> colours{
	std::array<float,3>{ 1.0f, 0.0f, 0.0f },
	std::array<float,3>{ 0.0f, 1.0f, 0.0f },
	std::array<float,3>{ 0.0f, 0.0f, 1.0f },
	std::array<float,3>{ 0.0f, 1.0f, 1.0f },
	std::array<float,3>{ 1.0f, 0.0f, 1.0f },
	std::array<float,3>{ 1.0f, 1.0f, 0.0f },
	std::array<float,3>{ 0.5f, 0.0f, 0.0f },
	std::array<float,3>{ 0.0f, 0.5f, 0.0f },
	std::array<float,3>{ 0.0f, 0.0f, 0.5f },
	std::array<float,3>{ 0.0f, 0.5f, 0.5f },
	std::array<float,3>{ 0.5f, 0.0f, 0.5f },
	std::array<float,3>{ 0.5f, 0.5f, 0.0f }
};

#include <boost/math/constants/constants.hpp>

class MidLatProjection
{
public:
	MidLatProjection(LatLon llc) :
		m_llc(llc),
		m_cosphi(llc.lat*boost::math::constants::pi<float>()/180.0f)
	{}

	void scale(float s){ m_scale=s; }

	std::array<double,2> operator()(LatLon p) const
	{
		return std::array<double,2>
		{
			m_scale*(p.lon-m_llc.lon)*m_cosphi,
			m_scale*(p.lat-m_llc.lat)
		};
	}

private:
	LatLon 	m_llc;
	float	m_cosphi;
	float	m_scale=1.0f;
};
void drawPoly(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<LatLon>& poly,bool fill=false);

void drawPolys(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<std::vector<LatLon>>& polys,bool fill=false)
{
	cr->save();
	cr->set_line_width(0.1f);

	for(unsigned i=0;i<polys.size();++i)
		drawPoly(cr,proj,polys[i],fill);
	cr->restore();
}

void drawFeatures(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<Feature>& F)
{
	cr->save();
	cr->set_line_width(0.1f);

	cr->set_source_rgb(0.0f,0.4f,0.6f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Lake || f.type() == River; }))
		drawPoly(cr,proj,f.points(),true);

	cr->set_source_rgb(0.8f,0.6f,0.2f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Island; }))
			drawPoly(cr,proj,f.points(),true);

	cr->set_source_rgb(0.0f,0.4f,0.6f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Stream; }))
		drawPoly(cr,proj,f.points(),false);

	cr->restore();
}

void drawPoly(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<LatLon>& poly,bool fill)
{
	array<double,2> xy;

	if (poly.size() > 0)
	{
		xy = proj(poly.front());
		cr->move_to(xy[0],xy[1]);
		for(const LatLon ll : poly)
		{
			xy = proj(ll);
			cr->line_to(xy[0],xy[1]);
		}
		if (fill)
		{
			cr->close_path();
			cr->fill();
		}
		else
			cr->stroke();
	}
}

int main(int argc,char **argv)
{
	XMLPlatform xmlp;

	string fn("/Users/jcassidy/src/ECE297/maps/hamilton_canada.osm.gz");
	string oFn("lake.pdf");

	if (argc > 1)
		fn=argv[1];

	if (argc > 2)
		oFn = argv[2];

	OSMDatabase db;

	db = loadOSM(fn);

	bool mapIsIsland=true;
	bool mapHasUnboundedLake=false;

	cout << "INFO: Starting on assumption that map is an island" << endl;

	bool image=false;

	////// Setup Cairo canvas

	std::array<unsigned,2> dims;

	if(image)
		dims = array<unsigned,2>{2048,1536};
	else
		dims = array<unsigned,2>{11*72,17*72/2};

	Cairo::RefPtr<Cairo::PdfSurface> surf = Cairo::PdfSurface::create(oFn,dims[0],dims[1]);

	Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surf);

	cr->save();

	double k;
	std::array<double,2> ddims{(double)dims[0],(double)dims[1]};
	std::array<double,2> offset;

	LatLon llc, urc;

	tie(llc,urc) = db.bounds();

	MidLatProjection proj(llc);
	pair<array<double,2>,array<double,2>> bb;
	bb = make_pair(
			proj(llc),
			proj(urc));

	assert(urc.lon > llc.lon);
	assert(urc.lat > llc.lat);

	double fillfactor=0.9;
	array<double,2> mapsize{bb.second[0]-bb.first[0], bb.second[1]-bb.first[1]};

	k = fillfactor/std::max(mapsize[0]/ddims[0], mapsize[1]/ddims[1]);

	proj.scale(k);

	bb = make_pair(
				proj(llc),
				proj(urc));

	offset = std::array<double,2>{ (ddims[0]-(bb.second[0]-bb.first[0]))*0.5, (ddims[1]-(bb.second[1]-bb.first[1]))*0.5 };

	cout << "  offset=" << offset[0] << ',' << offset[1] << " scale=" << k << endl;

	/** Cairo affine matrix is provided column-major as follows:
	 * [u] 		[	arg0	arg2	arg4]	[ x ]
	 * [v]	=	[	arg1	arg3	arg5]	[ y ]
	 * 										[ 1 ]
	 */

	Cairo::Matrix mat;

	mat = Cairo::Matrix(1,0,0,-1,offset[0],ddims[1]-offset[1]);

	cr->set_matrix(mat);

	cr->set_source_rgb(1.0,1.0,1.0);
	cr->fill();

	bb = make_pair(proj(llc), proj(urc));

	cr->set_source_rgb(0.0f,0.0f,0.0f);

	cr->rectangle(bb.first[0],bb.first[1],bb.second[0]-bb.first[0],bb.second[1]-bb.first[1]);
	cr->stroke();

	{
		array<double,2> uv;
		uv = proj(llc);
		mat.transform_point(uv[0],uv[1]);

		cout << "llc: (" << llc.lat << ',' << llc.lon << ") -> (" << uv[0] << ',' << uv[1] << ")" << endl;

		uv = proj(urc);
		mat.transform_point(uv[0],uv[1]);

		cout << "urc: (" << urc.lat << ',' << urc.lon << ") -> (" << uv[0] << ',' << uv[1] << ")" << endl;
	}

	////// Extract way water features

	vector<Feature> features;

	BasicWayFeatureFactory WF(db);

	for(const auto& w : db.ways())
	{
		auto f = WF(w);
		if (f)
			features.emplace_back(*f);
	}

	////// Extract lake multipolys and draw them

	cr->set_line_width(0.1f);
	cr->set_source_rgb(.0f,0.4f,0.6f);

	BasicRelationFeatureFactory FF(db);

	for(const auto& r : db.relations())
	{
		for(auto& feat : FF(r))
		{
			cout << "  Created feature (" << asString(feat.type()) << ") from relation ID " << r.id() << endl;

			if (!feat.bounded() && feat.isWater())
			{
				mapIsIsland=false;
				mapHasUnboundedLake=true;
				cout << "INFO: Found an unbounded water feature, so we conclude this is not an island" << endl;
			}
			features.emplace_back(std::move(feat));
		}
	}

	vector<const OSMWay*> coastline;
	unsigned m_kiNatural=db.wayTags().getIndexForKeyString("natural");
	unsigned m_viCoastline=db.wayTags().getIndexForValueString("coastline");

	// extract coastlines
	for(const auto& w : db.ways())
		if (w.hasTagWithValue(m_kiNatural,m_viCoastline))
			coastline.push_back(&w);

	cout << "Extracted " << coastline.size() << " ways with coastline tag" << endl;

	MultipolyCloser C(db,coastline);
	C.direction(MultipolyCloser::CCW);
	vector<pair<vector<LatLon>,bool>> F = C.loops(MultipolyCloser::All);

	mapIsIsland &= coastline.size()>0;

	for(const auto& poly : F)
		mapIsIsland &= poly.second;		// if none of the coastlines is unbounded, then we're looking at an island

	if (F.size() == 0)
	{
		cout << "INFO: No coastlines, so I conclude this is not an island" << endl;
		mapIsIsland=false;
	}

	if (mapIsIsland)
		cout << "INFO: There are " << F.size() << " coastline ways, none unbounded so I still think the map is an island" << endl;

	if (mapIsIsland)
	{
		cout << "INFO: Concluded the map is an island for lack of contradictory evidence" << endl;
		features.emplace_back(0,Relation,Lake,"<big ocean>",db.corners());
	}

	for(auto& poly : F)
	{
		if (poly.second)	// was originally closed
			features.emplace_back(0,Way,Island,"<unspecified>",std::move(poly.first));
		else
			features.emplace_back(0,Way,Lake,"<unspecified>",std::move(poly.first));
	}

	drawFeatures(cr,proj,features);
}


text:
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>

#include <glog/logging.h>
#include <gst/gst.h>
#include <boost/program_options.hpp>

#include "camera/camera_manager.h"
#include "common/context.h"
#include "model/model_manager.h"
#include "processor/image_classifier.h"
#include "processor/image_transformer.h"
#include "processor/neural_net_evaluator.h"
#include "processor/processor.h"

#include "tensorflow/core/framework/tensor.h"

namespace po = boost::program_options;

void Run(const std::string& camera_name, const std::string& net,
         const std::string& input_layer, const std::string& split_layer,
         const std::string& output_layer) {
  std::vector<std::shared_ptr<Processor>> procs;

  // Camera
  auto camera = CameraManager::GetInstance().GetCamera(camera_name);
  procs.push_back(camera);

  // Transformer
  auto model_desc = ModelManager::GetInstance().GetModelDesc(net);
  Shape input_shape(3, model_desc.GetInputWidth(), model_desc.GetInputHeight());
  auto transformer = std::make_shared<ImageTransformer>(input_shape, true);
  transformer->SetSource("input", camera->GetStream());
  transformer->SetBlockOnPush(true);
  procs.push_back(transformer);

  // NNE1
  std::vector<std::string> split_layers = {split_layer};
  auto nne1 = std::make_shared<NeuralNetEvaluator>(model_desc, input_shape, 1,
                                                   split_layers);
  nne1->SetSource(transformer->GetSink(), input_layer);
  nne1->SetBlockOnPush(true);
  procs.push_back(nne1);

  // NNE2
  std::vector<std::string> output_layers = {output_layer};
  auto nne2 = std::make_shared<NeuralNetEvaluator>(model_desc, input_shape, 1,
                                                   output_layers);
  nne2->SetSource(nne1->GetSink(), split_layer);
  nne2->SetBlockOnPush(true);
  procs.push_back(nne2);

  // add an classifier (NNC)
  auto classifier = std::make_shared<ImageClassifier>(model_desc, 1);
  classifier->SetSource("input", nne2->GetSink());
  procs.push_back(classifier);

  boost::posix_time::time_duration total_nne1_micros, total_nne2_micros, total_nne1_eval, total_nne2_eval,
                                   total_nne1_preproc_micros, total_nne2_preproc_micros;
  double total_rate = 0;
  int frame_count = 0;

  // Start the processors in reverse order.
  for (auto procs_it = procs.rbegin(); procs_it != procs.rend(); ++procs_it) {
    (*procs_it)->Start();
  }

  auto reader = classifier->GetSink("output")->Subscribe();
  tensorflow::Tensor split_tensor;

  while (true) {
    auto frame = reader->PopFrame();
    // Get NNE excution time
    total_nne1_micros += frame->GetValue<boost::posix_time::time_duration>("NeuralNetEvaluator.total_micros_1");
    total_nne2_micros += frame->GetValue<boost::posix_time::time_duration>("NeuralNetEvaluator.total_micros_2");
    total_nne1_eval += frame->GetValue<boost::posix_time::time_duration>("eval_micros_1");
    total_nne2_eval += frame->GetValue<boost::posix_time::time_duration>("eval_micros_2");

    total_nne1_preproc_micros += frame->GetValue<boost::posix_time::time_duration>("preproc_micros_1");
    total_nne2_preproc_micros += frame->GetValue<boost::posix_time::time_duration>("preproc_micros_2");

    frame_count += 1;
    auto last_id = frame->GetValue<unsigned long>("frame_id");
    split_tensor = frame->GetValue<tensorflow::Tensor>(split_layer);
    // Get Frame Rate
    total_rate += reader->GetPushFps();

    if (last_id > 2500) break;
  }

  // Stop the processors in forward order.
  for (const auto& proc : procs) {
    proc->Stop();
  }
  std::cout << "======" << std::endl;
  std::cout << "Frame count = " << frame_count << std::endl;
  std::cout << "Average NNE1 time = " << total_nne1_micros.total_microseconds() / frame_count << std::endl;
  std::cout << "Average NNE2 time = " << total_nne2_micros.total_microseconds() / frame_count << std::endl;

  std::cout << "Average NNE1 eval time = " << total_nne1_eval.total_microseconds() / frame_count << std::endl;
  std::cout << "Average NNE2 eval time = " << total_nne2_eval.total_microseconds() / frame_count << std::endl;

  std::cout << "Average NNE1 pproc time = " << total_nne1_preproc_micros.total_microseconds() / frame_count << std::endl;
  std::cout << "Average NNE2 pproc time = " << total_nne2_preproc_micros.total_microseconds() / frame_count << std::endl;

  std::cout << "total rate = " << total_rate / frame_count << std::endl;
  std::cout << split_tensor.dim_size(0)<< ","<<split_tensor.dim_size(1) << ","<< split_tensor.dim_size(2) << "," <<split_tensor.dim_size(3) << std::endl;
}

int main(int argc, char* argv[]) {
  po::options_description desc(
      "Demonstrates splitting DNN evaluation across two NNEs");
  desc.add_options()("help,h", "Print the help message.");
  desc.add_options()(
      "config-dir,C", po::value<std::string>(),
      "The directory containing streamer's configuration files.");
  desc.add_options()("camera,c", po::value<std::string>()->required(),
                     "The name of the camera to use.");
  desc.add_options()("net,n", po::value<std::string>()->required(),
                     "The name of the neural net to run.");
  desc.add_options()("input,i", po::value<std::string>()->required(),
                     "The name of the input layer of the neural net.");
  desc.add_options()("split,s", po::value<std::string>()->required(),
                     "The name of the layer after which to split computation.");
  desc.add_options()("output,o", po::value<std::string>()->required(),
                     "The name of the output layer of the neural net.");

  // Parse the command line arguments.
  po::variables_map args;
  try {
    po::store(po::parse_command_line(argc, argv, desc), args);
    if (args.count("help")) {
      std::cout << desc << std::endl;
      return 1;
    }
    po::notify(args);
  } catch (const po::error& e) {
    std::cerr << e.what() << std::endl;
    std::cout << desc << std::endl;
    return 1;
  }

  // Set up GStreamer.
  gst_init(&argc, &argv);
  // Set up glog.
  google::InitGoogleLogging(argv[0]);
  FLAGS_alsologtostderr = 1;
  FLAGS_colorlogtostderr = 1;
  // Initialize the streamer context. This must be called before using streamer.
  Context::GetContext().Init();

  // Extract the command line arguments.
  if (args.count("config-dir")) {
    Context::GetContext().SetConfigDir(args["config-dir"].as<std::string>());
  }
  std::string camera_name = args["camera"].as<std::string>();
  std::string net = args["net"].as<std::string>();
  std::string input = args["input"].as<std::string>();
  std::string split = args["split"].as<std::string>();
  std::string output = args["output"].as<std::string>();
  Run(camera_name, net, input, split, output);
  return 0;
}

max_stars_repo_name: GTkernel/streamer

max_issues_repo_name: GTkernel/streamer

max_forks_repo_name: GTkernel/streamer


text: #include <boost/config/abi_suffix.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino