<commit_before>/**
 * Clever programming language
 * Copyright (c) Clever Team
 *
 * This file is distributed under the MIT license. See LICENSE for details.
 */

#include <iostream>
#include "core/codegen.h"
#include "core/scope.h"
#include "core/compiler.h"

namespace clever { namespace ast {

static CLEVER_FORCE_INLINE void _prepare_operand(Operand& op, Node* node)
{
	if (node->isLiteral()) {
		op = Operand(FETCH_CONST, static_cast<Literal*>(node)->getConstId());
	} else if (node->getScope()) {
		op = Operand(FETCH_VAR, node->getValueId(),	node->getScope()->getId());
	} else {
		op = Operand(FETCH_TMP, node->getValueId());
	}
}

void Codegen::visit(NullLit* node)
{
	node->setConstId(0);
}

void Codegen::visit(IntLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(DoubleLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(StringLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(Ident* node)
{
	Symbol* sym = m_scope->getAny(node->getName());

	if (!sym) {
		Compiler::errorf(node->getLocation(),
			"Variable `%S' not found!", node->getName());
	}

	node->setValueId(sym->value_id);
	node->setScope(sym->scope);
}

void Codegen::visit(Block* node)
{
	m_scope = node->getScope();

	Visitor::visit(static_cast<NodeArray*>(node));

	m_scope = m_scope->getParent();
}

void Codegen::visit(CriticalBlock* node)
{
	m_scope = node->getScope();

	m_ir.push_back(IR(OP_LOCK));
	node->getBlock()->accept(*this);
	m_ir.push_back(IR(OP_UNLOCK));

	m_scope = m_scope->getParent();
}

void Codegen::visit(ThreadBlock* node)
{
	m_scope = node->getScope();

	size_t bg = m_ir.size();
	m_ir.push_back(IR(OP_BTHREAD, Operand(JMP_ADDR, bg)));

	node->getBlock()->accept(*this);

	m_ir.push_back(IR(OP_ETHREAD));

	m_ir[bg].op1 = Operand(JMP_ADDR, m_ir.size());

	m_scope = m_scope->getParent();
}

void Codegen::visit(VariableDecl* node)
{
	node->getAssignment()->accept(*this);
}

void Codegen::visit(Assignment* node)
{
	// TODO: allow assignment of any possible left hand side.
	Symbol* sym = static_cast<Ident*>(node->getLhs())->getSymbol();
	Node* rhs = node->getRhs();

	clever_assert_not_null(sym);

	if (node->isConditional()) {
		m_ir.push_back(IR(OP_JMPNZ,
			Operand(FETCH_VAR, sym->value_id, sym->scope->getId()),
			Operand(JMP_ADDR, m_ir.size() + 2)));
	}

	if (rhs) {
		rhs->accept(*this);
	}
	m_ir.push_back(IR(OP_ASSIGN,
			Operand(FETCH_VAR, sym->value_id, sym->scope->getId())));

	if (!rhs) {
		m_ir.back().op2 = Operand(FETCH_CONST, 0); // null
	} else {
		_prepare_operand(m_ir.back().op2, rhs);
	}
}

void Codegen::visit(FunctionCall* node)
{
	// TODO: allow call for any possible callee
	Symbol* sym = static_cast<Ident*>(node->getCallee())->getSymbol();

	clever_assert_not_null(sym);

	if (node->hasArgs()) {
		NodeList& args = node->getArgs()->getNodes();
		NodeList::const_iterator it = args.begin(), end = args.end();

		while (it != end) {
			(*it)->accept(*this);

			m_ir.push_back(IR(OP_SEND_VAL));

			_prepare_operand(m_ir.back().op1, *it);
			++it;
		}
	}

	m_ir.push_back(IR(OP_FCALL,
		Operand(FETCH_VAR, sym->value_id, sym->scope->getId())));

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(FunctionDecl* node)
{
	size_t start_func = m_ir.size();

	m_ir.push_back(IR(OP_JMP, Operand(JMP_ADDR, 0)));

	Symbol* sym = node->getIdent()->getSymbol();
	Value* funcval = sym->scope->getValue(sym->value_id);
	Function* func = static_cast<Function*>(funcval->getObj());
	func->setAddr(m_ir.size());

	m_scope = node->getScope();

	if (node->hasArgs()) {
		node->getArgs()->accept(*this);
	}

	node->getBlock()->accept(*this);

	m_scope = m_scope->getParent();

	m_ir.push_back(IR(OP_LEAVE));

	m_ir[start_func].op1.value_id = m_ir.size();

	if (node->isAnonymous()) {
		node->setValueId(sym->value_id);
		node->setScope(sym->scope);
	}
}

void Codegen::visit(Return* node)
{
	if (node->hasValue()) {
		Node* value = node->getValue();

		value->accept(*this);

		m_ir.push_back(IR(OP_RET));

		_prepare_operand(m_ir.back().op1, value);
	} else {
		m_ir.push_back(IR(OP_RET));
	}
}

void Codegen::visit(While* node)
{
	Node* cond = node->getCondition();
	size_t start_while = m_ir.size();

	cond->accept(*this);

	size_t jmp_addr = m_ir.size();

	m_ir.push_back(IR(OP_JMPZ));

	_prepare_operand(m_ir.back().op1, cond);

	node->getBlock()->accept(*this);

	m_ir.push_back(IR(OP_JMP, Operand(JMP_ADDR, start_while)));

	m_ir[jmp_addr].op2 = Operand(JMP_ADDR, m_ir.size());
}

void Codegen::visit(IncDec* node)
{
	Opcode op = OP_PRE_INC;

	node->getVar()->accept(*this);

	switch (node->getOperator()) {
		case IncDec::PRE_INC: op = OP_PRE_INC; break;
		case IncDec::PRE_DEC: op = OP_PRE_DEC; break;
		case IncDec::POS_INC: op = OP_POS_INC; break;
		case IncDec::POS_DEC: op = OP_POS_DEC; break;
	}

	m_ir.push_back(IR(op,
		Operand(FETCH_VAR, node->getVar()->getValueId(),
			node->getVar()->getScope()->getId())));

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(Arithmetic* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_ADD;

	switch (node->getOperator()) {
		case Arithmetic::MOP_ADD: op = OP_ADD; break;
		case Arithmetic::MOP_SUB: op = OP_SUB; break;
		case Arithmetic::MOP_MUL: op = OP_MUL; break;
		case Arithmetic::MOP_DIV: op = OP_DIV; break;
		case Arithmetic::MOP_MOD: op = OP_MOD; break;
	}

	lhs->accept(*this);
	rhs->accept(*this);

	m_ir.push_back(IR(op));

	_prepare_operand(m_ir.back().op1, lhs);
	_prepare_operand(m_ir.back().op2, rhs);

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(Logic* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_EQUAL;

	switch (node->getOperator()) {
		case Logic::LOP_EQUALS:  op = OP_EQUAL;  break;
		case Logic::LOP_NEQUALS: op = OP_NEQUAL; break;
		case Logic::LOP_AND:     op = OP_AND;    break;
		case Logic::LOP_OR:      op = OP_OR;     break;
	}

	lhs->accept(*this);

	if (op == OP_AND || op == OP_OR) {
		size_t jmp_ir = m_ir.size();
		size_t res_id = m_compiler->getTempValue();

		node->setValueId(res_id);

		m_ir.push_back(IR(op));
		_prepare_operand(m_ir.back().op1, lhs);
		m_ir.back().result = Operand(FETCH_TMP, res_id);

		rhs->accept(*this);

		m_ir.push_back(IR(op));
		_prepare_operand(m_ir.back().op1, rhs);
		m_ir.back().result = Operand(FETCH_TMP, res_id);

		m_ir.back().op2 = m_ir[jmp_ir].op2 = Operand(JMP_ADDR, m_ir.size());
	} else {
		rhs->accept(*this);

		m_ir.push_back(IR(op));

		_prepare_operand(m_ir.back().op1, lhs);
		_prepare_operand(m_ir.back().op2, rhs);

		size_t tmp_id = m_compiler->getTempValue();

		m_ir.back().result = Operand(FETCH_TMP, tmp_id);

		node->setValueId(tmp_id);
	}
}

void Codegen::visit(Boolean* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_JMPZ;

	switch (node->getOperator()) {
		case Boolean::BOP_AND: op = OP_JMPZ;  break;
		case Boolean::BOP_OR:  op = OP_JMPNZ; break;
	}

	lhs->accept(*this);

	size_t jmp_ir = m_ir.size();
	size_t res_id = m_compiler->getTempValue();

	node->setValueId(res_id);

	m_ir.push_back(IR(op));
	_prepare_operand(m_ir.back().op1, lhs);
	m_ir.back().result = Operand(FETCH_TMP, res_id);

	rhs->accept(*this);

	m_ir.push_back(IR(op));
	_prepare_operand(m_ir.back().op1, rhs);
	m_ir.back().result = Operand(FETCH_TMP, res_id);

	m_ir.back().op2 = m_ir[jmp_ir].op2 = Operand(JMP_ADDR, m_ir.size());
}

void Codegen::visit(If* node)
{
	size_t last_jmpz = 0;
	std::vector<std::pair<Node*, Node*> >& branches = node->getConditionals();
	std::vector<std::pair<Node*, Node*> >::const_iterator it(branches.begin()),
		end(branches.end());

	m_jmps.push(AddrVector());

	while (it != end) {
		Node* cond  = (*it).first;
		Node* block = (*it).second;

		cond->accept(*this);

		last_jmpz = m_ir.size();
		m_ir.push_back(IR(OP_JMPZ));

		_prepare_operand(m_ir.back().op1, cond);

		block->accept(*this);

		m_jmps.top().push_back(m_ir.size());
		m_ir.push_back(IR(OP_JMP)); // JMP to outside if-elseif-else

		m_ir[last_jmpz].op2 = Operand(JMP_ADDR, m_ir.size());

		++it;
	}

	if (node->getElseNode()) {
		node->getElseNode()->accept(*this);
	}

	AddrVector& vec = m_jmps.top();
	for (size_t i = 0, j = vec.size(); i < j; ++i) {
		m_ir[vec[i]].op1 = Operand(JMP_ADDR, m_ir.size());
	}

	m_jmps.pop();
}

}} // clever::ast
<commit_msg>Thread and critical block codegen fixed<commit_after>/**
 * Clever programming language
 * Copyright (c) Clever Team
 *
 * This file is distributed under the MIT license. See LICENSE for details.
 */

#include <iostream>
#include "core/codegen.h"
#include "core/scope.h"
#include "core/compiler.h"

namespace clever { namespace ast {

static CLEVER_FORCE_INLINE void _prepare_operand(Operand& op, Node* node)
{
	if (node->isLiteral()) {
		op = Operand(FETCH_CONST, static_cast<Literal*>(node)->getConstId());
	} else if (node->getScope()) {
		op = Operand(FETCH_VAR, node->getValueId(),	node->getScope()->getId());
	} else {
		op = Operand(FETCH_TMP, node->getValueId());
	}
}

void Codegen::visit(NullLit* node)
{
	node->setConstId(0);
}

void Codegen::visit(IntLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(DoubleLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(StringLit* node)
{
	node->setConstId(m_compiler->addConstant(new Value(node->getValue())));
}

void Codegen::visit(Ident* node)
{
	Symbol* sym = m_scope->getAny(node->getName());

	if (!sym) {
		Compiler::errorf(node->getLocation(),
			"Variable `%S' not found!", node->getName());
	}

	node->setValueId(sym->value_id);
	node->setScope(sym->scope);
}

void Codegen::visit(Block* node)
{
	m_scope = node->getScope();

	Visitor::visit(static_cast<NodeArray*>(node));

	m_scope = m_scope->getParent();
}

void Codegen::visit(CriticalBlock* node)
{
	m_ir.push_back(IR(OP_LOCK));
	node->getBlock()->accept(*this);
	m_ir.push_back(IR(OP_UNLOCK));
}

void Codegen::visit(ThreadBlock* node)
{
	size_t bg = m_ir.size();
	m_ir.push_back(IR(OP_BTHREAD, Operand(JMP_ADDR, bg)));

	node->getBlock()->accept(*this);

	m_ir.push_back(IR(OP_ETHREAD));

	m_ir[bg].op1 = Operand(JMP_ADDR, m_ir.size());
}

void Codegen::visit(VariableDecl* node)
{
	node->getAssignment()->accept(*this);
}

void Codegen::visit(Assignment* node)
{
	// TODO: allow assignment of any possible left hand side.
	Symbol* sym = static_cast<Ident*>(node->getLhs())->getSymbol();
	Node* rhs = node->getRhs();

	clever_assert_not_null(sym);

	if (node->isConditional()) {
		m_ir.push_back(IR(OP_JMPNZ,
			Operand(FETCH_VAR, sym->value_id, sym->scope->getId()),
			Operand(JMP_ADDR, m_ir.size() + 2)));
	}

	if (rhs) {
		rhs->accept(*this);
	}
	m_ir.push_back(IR(OP_ASSIGN,
			Operand(FETCH_VAR, sym->value_id, sym->scope->getId())));

	if (!rhs) {
		m_ir.back().op2 = Operand(FETCH_CONST, 0); // null
	} else {
		_prepare_operand(m_ir.back().op2, rhs);
	}
}

void Codegen::visit(FunctionCall* node)
{
	// TODO: allow call for any possible callee
	Symbol* sym = static_cast<Ident*>(node->getCallee())->getSymbol();

	clever_assert_not_null(sym);

	if (node->hasArgs()) {
		NodeList& args = node->getArgs()->getNodes();
		NodeList::const_iterator it = args.begin(), end = args.end();

		while (it != end) {
			(*it)->accept(*this);

			m_ir.push_back(IR(OP_SEND_VAL));

			_prepare_operand(m_ir.back().op1, *it);
			++it;
		}
	}

	m_ir.push_back(IR(OP_FCALL,
		Operand(FETCH_VAR, sym->value_id, sym->scope->getId())));

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(FunctionDecl* node)
{
	size_t start_func = m_ir.size();

	m_ir.push_back(IR(OP_JMP, Operand(JMP_ADDR, 0)));

	Symbol* sym = node->getIdent()->getSymbol();
	Value* funcval = sym->scope->getValue(sym->value_id);
	Function* func = static_cast<Function*>(funcval->getObj());
	func->setAddr(m_ir.size());

	m_scope = node->getScope();

	if (node->hasArgs()) {
		node->getArgs()->accept(*this);
	}

	node->getBlock()->accept(*this);

	m_scope = m_scope->getParent();

	m_ir.push_back(IR(OP_LEAVE));

	m_ir[start_func].op1.value_id = m_ir.size();

	if (node->isAnonymous()) {
		node->setValueId(sym->value_id);
		node->setScope(sym->scope);
	}
}

void Codegen::visit(Return* node)
{
	if (node->hasValue()) {
		Node* value = node->getValue();

		value->accept(*this);

		m_ir.push_back(IR(OP_RET));

		_prepare_operand(m_ir.back().op1, value);
	} else {
		m_ir.push_back(IR(OP_RET));
	}
}

void Codegen::visit(While* node)
{
	Node* cond = node->getCondition();
	size_t start_while = m_ir.size();

	cond->accept(*this);

	size_t jmp_addr = m_ir.size();

	m_ir.push_back(IR(OP_JMPZ));

	_prepare_operand(m_ir.back().op1, cond);

	node->getBlock()->accept(*this);

	m_ir.push_back(IR(OP_JMP, Operand(JMP_ADDR, start_while)));

	m_ir[jmp_addr].op2 = Operand(JMP_ADDR, m_ir.size());
}

void Codegen::visit(IncDec* node)
{
	Opcode op = OP_PRE_INC;

	node->getVar()->accept(*this);

	switch (node->getOperator()) {
		case IncDec::PRE_INC: op = OP_PRE_INC; break;
		case IncDec::PRE_DEC: op = OP_PRE_DEC; break;
		case IncDec::POS_INC: op = OP_POS_INC; break;
		case IncDec::POS_DEC: op = OP_POS_DEC; break;
	}

	m_ir.push_back(IR(op,
		Operand(FETCH_VAR, node->getVar()->getValueId(),
			node->getVar()->getScope()->getId())));

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(Arithmetic* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_ADD;

	switch (node->getOperator()) {
		case Arithmetic::MOP_ADD: op = OP_ADD; break;
		case Arithmetic::MOP_SUB: op = OP_SUB; break;
		case Arithmetic::MOP_MUL: op = OP_MUL; break;
		case Arithmetic::MOP_DIV: op = OP_DIV; break;
		case Arithmetic::MOP_MOD: op = OP_MOD; break;
	}

	lhs->accept(*this);
	rhs->accept(*this);

	m_ir.push_back(IR(op));

	_prepare_operand(m_ir.back().op1, lhs);
	_prepare_operand(m_ir.back().op2, rhs);

	size_t tmp_id = m_compiler->getTempValue();

	m_ir.back().result = Operand(FETCH_TMP, tmp_id);

	node->setValueId(tmp_id);
}

void Codegen::visit(Logic* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_EQUAL;

	switch (node->getOperator()) {
		case Logic::LOP_EQUALS:  op = OP_EQUAL;  break;
		case Logic::LOP_NEQUALS: op = OP_NEQUAL; break;
		case Logic::LOP_AND:     op = OP_AND;    break;
		case Logic::LOP_OR:      op = OP_OR;     break;
	}

	lhs->accept(*this);

	if (op == OP_AND || op == OP_OR) {
		size_t jmp_ir = m_ir.size();
		size_t res_id = m_compiler->getTempValue();

		node->setValueId(res_id);

		m_ir.push_back(IR(op));
		_prepare_operand(m_ir.back().op1, lhs);
		m_ir.back().result = Operand(FETCH_TMP, res_id);

		rhs->accept(*this);

		m_ir.push_back(IR(op));
		_prepare_operand(m_ir.back().op1, rhs);
		m_ir.back().result = Operand(FETCH_TMP, res_id);

		m_ir.back().op2 = m_ir[jmp_ir].op2 = Operand(JMP_ADDR, m_ir.size());
	} else {
		rhs->accept(*this);

		m_ir.push_back(IR(op));

		_prepare_operand(m_ir.back().op1, lhs);
		_prepare_operand(m_ir.back().op2, rhs);

		size_t tmp_id = m_compiler->getTempValue();

		m_ir.back().result = Operand(FETCH_TMP, tmp_id);

		node->setValueId(tmp_id);
	}
}

void Codegen::visit(Boolean* node)
{
	Node* lhs = node->getLhs();
	Node* rhs = node->getRhs();
	Opcode op = OP_JMPZ;

	switch (node->getOperator()) {
		case Boolean::BOP_AND: op = OP_JMPZ;  break;
		case Boolean::BOP_OR:  op = OP_JMPNZ; break;
	}

	lhs->accept(*this);

	size_t jmp_ir = m_ir.size();
	size_t res_id = m_compiler->getTempValue();

	node->setValueId(res_id);

	m_ir.push_back(IR(op));
	_prepare_operand(m_ir.back().op1, lhs);
	m_ir.back().result = Operand(FETCH_TMP, res_id);

	rhs->accept(*this);

	m_ir.push_back(IR(op));
	_prepare_operand(m_ir.back().op1, rhs);
	m_ir.back().result = Operand(FETCH_TMP, res_id);

	m_ir.back().op2 = m_ir[jmp_ir].op2 = Operand(JMP_ADDR, m_ir.size());
}

void Codegen::visit(If* node)
{
	size_t last_jmpz = 0;
	std::vector<std::pair<Node*, Node*> >& branches = node->getConditionals();
	std::vector<std::pair<Node*, Node*> >::const_iterator it(branches.begin()),
		end(branches.end());

	m_jmps.push(AddrVector());

	while (it != end) {
		Node* cond  = (*it).first;
		Node* block = (*it).second;

		cond->accept(*this);

		last_jmpz = m_ir.size();
		m_ir.push_back(IR(OP_JMPZ));

		_prepare_operand(m_ir.back().op1, cond);

		block->accept(*this);

		m_jmps.top().push_back(m_ir.size());
		m_ir.push_back(IR(OP_JMP)); // JMP to outside if-elseif-else

		m_ir[last_jmpz].op2 = Operand(JMP_ADDR, m_ir.size());

		++it;
	}

	if (node->getElseNode()) {
		node->getElseNode()->accept(*this);
	}

	AddrVector& vec = m_jmps.top();
	for (size_t i = 0, j = vec.size(); i < j; ++i) {
		m_ir[vec[i]].op1 = Operand(JMP_ADDR, m_ir.size());
	}

	m_jmps.pop();
}

}} // clever::ast
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE "test_read_harmonic_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
#include <mjolnir/input/read_local_potential.hpp>
#include <mjolnir/input/read_global_potential.hpp>
#include <mjolnir/input/read_external_potential.hpp>
#include <tuple>

using test_types = std::tuple<double, float>;

constexpr inline float  tolerance_value(float)  noexcept {return 1e-4;}
constexpr inline double tolerance_value(double) noexcept {return 1e-8;}

template<typename Real>
decltype(boost::test_tools::tolerance(std::declval<Real>()))
tolerance() {return boost::test_tools::tolerance(tolerance_value(Real()));}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_local_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            parameters = [
                {indices = [1, 2],              k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  1, k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset = -1, k = 3.14, v0 = 2.71},
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<2,
            mjolnir::HarmonicPotential<real_type>>(v);

        BOOST_TEST(g.size() == 3u);
        BOOST_TEST(g.at(0).first[0] == 1u);
        BOOST_TEST(g.at(0).first[1] == 2u);

        BOOST_TEST(g.at(1).first[0] == 2u);
        BOOST_TEST(g.at(1).first[1] == 3u);

        BOOST_TEST(g.at(2).first[0] == 0u);
        BOOST_TEST(g.at(2).first[1] == 1u);

        BOOST_TEST(g.at(0).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(1).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(1).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(2).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(2).second.v0() == real_type(2.71));
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_list_local_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            parameters = [
                {indices = [1, 2],                    k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  [1, -1], k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  [-1, 1], k = 3.14, v0 = 2.71},
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<2,
            mjolnir::HarmonicPotential<real_type>>(v);

        BOOST_TEST(g.size() == 3u);
        BOOST_TEST(g.at(0).first[0] == 1u);
        BOOST_TEST(g.at(0).first[1] == 2u);

        BOOST_TEST(g.at(1).first[0] == 2u);
        BOOST_TEST(g.at(1).first[1] == 1u);

        BOOST_TEST(g.at(2).first[0] == 0u);
        BOOST_TEST(g.at(2).first[1] == 3u);

        BOOST_TEST(g.at(0).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(1).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(1).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(2).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(2).second.v0() == real_type(2.71));
    }
}
BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_global_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    using traits_type = mjolnir::SimulatorTraits<real_type, mjolnir::UnlimitedBoundary>;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            interaction = "Pair"
            potential   = "LennardJones"
            spatial_partition.type  = "Naive"
            parameters = [
                {index =   0,               sigma =   2.0, epsilon = 1.5},
                {index =   5, offset =   1, sigma =   5.0, epsilon = 0.5},
                {index =  10, offset =  -1, sigma =   7.0, epsilon = 0.7},
                {index = 100, offset = 100, sigma = 100.0, epsilon = 0.1},
            ]
        )"_toml;

        const auto pot = mjolnir::read_lennard_jones_potential<traits_type>(v);

        BOOST_TEST(!pot.exclusion_list().is_ignored_molecule(0, 0));
        BOOST_TEST(!pot.exclusion_list().is_ignored_molecule(0, 1));
        BOOST_TEST(!pot.exclusion_list().is_ignored_molecule(1, 1));

        BOOST_TEST(pot.participants().size() ==   4u);
        BOOST_TEST(pot.participants().at(0)  ==   0u);
        BOOST_TEST(pot.participants().at(1)  ==   6u);
        BOOST_TEST(pot.participants().at(2)  ==   9u);
        BOOST_TEST(pot.participants().at(3)  == 200u);

        BOOST_TEST(pot.parameters().at(  0).first  == real_type(  2.0), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(  6).first  == real_type(  5.0), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(  9).first  == real_type(  7.0), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(200).first  == real_type(100.0), tolerance<real_type>());

        BOOST_TEST(pot.parameters().at(  0).second == real_type(1.5), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(  6).second == real_type(0.5), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(  9).second == real_type(0.7), tolerance<real_type>());
        BOOST_TEST(pot.parameters().at(200).second == real_type(0.1), tolerance<real_type>());
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_external_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    using real_type = T;
    {
        using namespace toml::literals;
        const toml::value v = u8R"(
            interaction    = "Distance"
            potential      = "LennardJonesWall"
            shape.name     = "AxisAlignedPlane"
            shape.axis     = "+X"
            shape.position = 1.0
            shape.margin   = 0.5
            cutoff         = 3.14
            parameters     = [
                {index =  0,               sigma = 1.0, epsilon = 1.0},
                {index =  2, offset =   1, sigma = 2.0, epsilon = 2.0},
                {index =  9, offset =  -1, sigma = 3.0, epsilon = 3.0},
                {index = 10, offset =  10, sigma = 4.0, epsilon = 4.0},
            ]
        )"_toml;

        const auto g = mjolnir::read_lennard_jones_wall_potential<real_type>(v);

        BOOST_TEST(g.participants().size() ==  4u);
        BOOST_TEST(g.participants().at(0)  ==  0u);
        BOOST_TEST(g.participants().at(1)  ==  3u);
        BOOST_TEST(g.participants().at(2)  ==  8u);
        BOOST_TEST(g.participants().at(3)  == 20u);

        BOOST_TEST(g.parameters().at( 0).first  == real_type(1.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 3).first  == real_type(2.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 8).first  == real_type(3.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at(20).first  == real_type(4.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 0).second == real_type(1.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 3).second == real_type(2.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 8).second == real_type(3.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at(20).second == real_type(4.0), tolerance<real_type>());
        BOOST_TEST(g.cutoff() == real_type(3.14), tolerance<real_type>());
    }
}
<commit_msg>test: update test_read_offset<commit_after>#define BOOST_TEST_MODULE "test_read_harmonic_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
#include <mjolnir/input/read_local_potential.hpp>
#include <mjolnir/input/read_global_potential.hpp>
#include <mjolnir/input/read_external_potential.hpp>
#include <tuple>

using test_types = std::tuple<double, float>;

constexpr inline float  tolerance_value(float)  noexcept {return 1e-4;}
constexpr inline double tolerance_value(double) noexcept {return 1e-8;}

template<typename Real>
decltype(boost::test_tools::tolerance(std::declval<Real>()))
tolerance() {return boost::test_tools::tolerance(tolerance_value(Real()));}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_local_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            parameters = [
                {indices = [1, 2],              k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  1, k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset = -1, k = 3.14, v0 = 2.71},
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<2,
            mjolnir::HarmonicPotential<real_type>>(v);

        BOOST_TEST(g.size() == 3u);
        BOOST_TEST(g.at(0).first[0] == 1u);
        BOOST_TEST(g.at(0).first[1] == 2u);

        BOOST_TEST(g.at(1).first[0] == 2u);
        BOOST_TEST(g.at(1).first[1] == 3u);

        BOOST_TEST(g.at(2).first[0] == 0u);
        BOOST_TEST(g.at(2).first[1] == 1u);

        BOOST_TEST(g.at(0).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(1).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(1).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(2).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(2).second.v0() == real_type(2.71));
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_list_local_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            parameters = [
                {indices = [1, 2],                    k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  [1, -1], k = 3.14, v0 = 2.71},
                {indices = [1, 2], offset =  [-1, 1], k = 3.14, v0 = 2.71},
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<2,
            mjolnir::HarmonicPotential<real_type>>(v);

        BOOST_TEST(g.size() == 3u);
        BOOST_TEST(g.at(0).first[0] == 1u);
        BOOST_TEST(g.at(0).first[1] == 2u);

        BOOST_TEST(g.at(1).first[0] == 2u);
        BOOST_TEST(g.at(1).first[1] == 1u);

        BOOST_TEST(g.at(2).first[0] == 0u);
        BOOST_TEST(g.at(2).first[1] == 3u);

        BOOST_TEST(g.at(0).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(1).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(1).second.v0() == real_type(2.71));
        BOOST_TEST(g.at(2).second.k()  == real_type(3.14));
        BOOST_TEST(g.at(2).second.v0() == real_type(2.71));
    }
}
BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_global_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    using traits_type = mjolnir::SimulatorTraits<real_type, mjolnir::UnlimitedBoundary>;
    {
        using namespace toml::literals;
        const auto v = u8R"(
            interaction = "Pair"
            potential   = "LennardJones"
            spatial_partition.type  = "Naive"
            parameters = [
                {index =   0,               sigma =   2.0, epsilon = 1.5},
                {index =   5, offset =   1, sigma =   5.0, epsilon = 0.5},
                {index =  10, offset =  -1, sigma =   7.0, epsilon = 0.7},
                {index = 100, offset = 100, sigma = 100.0, epsilon = 0.1},
            ]
        )"_toml;

        const auto parameter_list = mjolnir::read_lennard_jones_potential<traits_type>(v);

        BOOST_TEST(!parameter_list.exclusion_list().is_ignored_molecule(0, 0));
        BOOST_TEST(!parameter_list.exclusion_list().is_ignored_molecule(0, 1));
        BOOST_TEST(!parameter_list.exclusion_list().is_ignored_molecule(1, 1));

        BOOST_TEST(parameter_list.participants().size() ==   4u);
        BOOST_TEST(parameter_list.participants().at(0)  ==   0u);
        BOOST_TEST(parameter_list.participants().at(1)  ==   6u);
        BOOST_TEST(parameter_list.participants().at(2)  ==   9u);
        BOOST_TEST(parameter_list.participants().at(3)  == 200u);

        const auto& lorentz_bertherot = dynamic_cast<
            mjolnir::LorentzBerthelotRule<traits_type, mjolnir::LennardJonesPotential<real_type>> const&>(
                parameter_list.cref());

        BOOST_TEST(lorentz_bertherot.parameters().at(  0).first  == real_type(  2.0), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(  6).first  == real_type(  5.0), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(  9).first  == real_type(  7.0), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(200).first  == real_type(100.0), tolerance<real_type>());

        BOOST_TEST(lorentz_bertherot.parameters().at(  0).second == real_type(1.5), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(  6).second == real_type(0.5), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(  9).second == real_type(0.7), tolerance<real_type>());
        BOOST_TEST(lorentz_bertherot.parameters().at(200).second == real_type(0.1), tolerance<real_type>());
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_index_offset_external_potential, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_index_offset.log");

    using real_type = T;
    using real_type = T;
    {
        using namespace toml::literals;
        const toml::value v = u8R"(
            interaction    = "Distance"
            potential      = "LennardJonesWall"
            shape.name     = "AxisAlignedPlane"
            shape.axis     = "+X"
            shape.position = 1.0
            shape.margin   = 0.5
            cutoff         = 3.14
            parameters     = [
                {index =  0,               sigma = 1.0, epsilon = 1.0},
                {index =  2, offset =   1, sigma = 2.0, epsilon = 2.0},
                {index =  9, offset =  -1, sigma = 3.0, epsilon = 3.0},
                {index = 10, offset =  10, sigma = 4.0, epsilon = 4.0},
            ]
        )"_toml;

        const auto g = mjolnir::read_lennard_jones_wall_potential<real_type>(v);

        BOOST_TEST(g.participants().size() ==  4u);
        BOOST_TEST(g.participants().at(0)  ==  0u);
        BOOST_TEST(g.participants().at(1)  ==  3u);
        BOOST_TEST(g.participants().at(2)  ==  8u);
        BOOST_TEST(g.participants().at(3)  == 20u);

        BOOST_TEST(g.parameters().at( 0).first  == real_type(1.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 3).first  == real_type(2.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 8).first  == real_type(3.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at(20).first  == real_type(4.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 0).second == real_type(1.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 3).second == real_type(2.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at( 8).second == real_type(3.0), tolerance<real_type>());
        BOOST_TEST(g.parameters().at(20).second == real_type(4.0), tolerance<real_type>());
        BOOST_TEST(g.cutoff() == real_type(3.14), tolerance<real_type>());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/importer/importer_host.h"

#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/importer/firefox_profile_lock.h"
#include "chrome/browser/importer/importer.h"
#include "chrome/browser/importer/importer_lock_dialog.h"
#include "chrome/browser/importer/importer_progress_observer.h"
#include "chrome/browser/importer/importer_type.h"
#include "chrome/browser/importer/in_process_importer_bridge.h"
#include "chrome/browser/importer/toolbar_importer_utils.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/public/browser/notification_source.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

#if defined(OS_WIN)
// TODO(port): Port this file.
#include "ui/base/message_box_win.h"
#endif

ImporterHost::ImporterHost()
    : profile_(NULL),
      waiting_for_bookmarkbar_model_(false),
      installed_bookmark_observer_(false),
      is_source_readable_(true),
      importer_(NULL),
      headless_(false),
      parent_window_(NULL),
      observer_(NULL) {
}

void ImporterHost::ShowWarningDialog() {
  if (headless_)
    OnImportLockDialogEnd(false);
  else
    importer::ShowImportLockDialog(parent_window_, this);
}

void ImporterHost::OnImportLockDialogEnd(bool is_continue) {
  if (is_continue) {
    // User chose to continue, then we check the lock again to make
    // sure that Firefox has been closed. Try to import the settings
    // if successful. Otherwise, show a warning dialog.
    firefox_lock_->Lock();
    if (firefox_lock_->HasAcquired()) {
      is_source_readable_ = true;
      InvokeTaskIfDone();
    } else {
      ShowWarningDialog();
    }
  } else {
    // User chose to skip the import process. We should reset the |task_| and
    // notify the ImporterHost to finish.
    task_ = base::Closure();
    importer_ = NULL;
    NotifyImportEnded();
  }
}

void ImporterHost::SetObserver(importer::ImporterProgressObserver* observer) {
  observer_ = observer;
}

void ImporterHost::NotifyImportStarted() {
  if (observer_)
    observer_->ImportStarted();
}

void ImporterHost::NotifyImportItemStarted(importer::ImportItem item) {
  if (observer_)
    observer_->ImportItemStarted(item);
}

void ImporterHost::NotifyImportItemEnded(importer::ImportItem item) {
  if (observer_)
    observer_->ImportItemEnded(item);
}

void ImporterHost::NotifyImportEnded() {
  firefox_lock_.reset();  // Release the Firefox profile lock.
  if (observer_)
    observer_->ImportEnded();
  Release();
}

void ImporterHost::StartImportSettings(
    const importer::SourceProfile& source_profile,
    Profile* target_profile,
    uint16 items,
    ProfileWriter* writer,
    bool first_run) {
  // We really only support importing from one host at a time.
  DCHECK(!profile_);
  DCHECK(target_profile);

  profile_ = target_profile;
  PrefService* user_prefs = profile_->GetPrefs();

  // Make sure only items that were not disabled by policy are imported.
  if (!user_prefs->GetBoolean(prefs::kImportHistory))
    items &= ~importer::HISTORY;
  if (!user_prefs->GetBoolean(prefs::kImportSearchEngine))
    items &= ~importer::SEARCH_ENGINES;
  if (!user_prefs->GetBoolean(prefs::kImportBookmarks))
    items &= ~importer::FAVORITES;
  if (!user_prefs->GetBoolean(prefs::kImportSavedPasswords))
    items &= ~importer::PASSWORDS;

  // Preserves the observer and creates a task, since we do async import so that
  // it doesn't block the UI. When the import is complete, observer will be
  // notified.
  writer_ = writer;
  importer_ = importer::CreateImporterByType(source_profile.importer_type);
  // If we fail to create the Importer, exit, as we cannot do anything.
  if (!importer_) {
    NotifyImportEnded();
    return;
  }

  importer_->AddRef();

  scoped_refptr<InProcessImporterBridge> bridge(
      new InProcessImporterBridge(writer_.get(), this));
  task_ = base::Bind(
      &Importer::StartImport, importer_, source_profile, items, bridge);

  CheckForFirefoxLock(source_profile, items, first_run);

#if defined(OS_WIN)
  // For google toolbar import, we need the user to log in and store their GAIA
  // credentials.
  if (source_profile.importer_type == importer::TYPE_GOOGLE_TOOLBAR5) {
    toolbar_importer_utils::IsGoogleGAIACookieInstalled(
        base::Bind(&ImporterHost::OnGoogleGAIACookieChecked, this), profile_);
    is_source_readable_ = false;
  }
#endif

  CheckForLoadedModels(items);
  AddRef();
  InvokeTaskIfDone();
}

void ImporterHost::OnGoogleGAIACookieChecked(bool result) {
#if defined(OS_WIN)
  if (!result) {
    ui::MessageBox(
        NULL,
        UTF16ToWide(l10n_util::GetStringUTF16(
            IDS_IMPORTER_GOOGLE_LOGIN_TEXT)).c_str(),
        L"",
        MB_OK | MB_TOPMOST);

    GURL url("https://www.google.com/accounts/ServiceLogin");
    BrowserList::GetLastActive()->AddSelectedTabWithURL(
        url, content::PAGE_TRANSITION_TYPED);

    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
        &ImporterHost::OnImportLockDialogEnd, this, false));
  } else {
    is_source_readable_ = true;
    InvokeTaskIfDone();
  }
#endif
}

void ImporterHost::Cancel() {
  if (importer_)
    importer_->Cancel();
}

ImporterHost::~ImporterHost() {
  if (NULL != importer_)
    importer_->Release();

  if (installed_bookmark_observer_) {
    DCHECK(profile_);
    profile_->GetBookmarkModel()->RemoveObserver(this);
  }
}

void ImporterHost::CheckForFirefoxLock(
    const importer::SourceProfile& source_profile,
    uint16 items,
    bool first_run) {
  if (source_profile.importer_type == importer::TYPE_FIREFOX2 ||
      source_profile.importer_type == importer::TYPE_FIREFOX3) {
    DCHECK(!firefox_lock_.get());
    firefox_lock_.reset(new FirefoxProfileLock(source_profile.source_path));
    if (!firefox_lock_->HasAcquired()) {
      // If fail to acquire the lock, we set the source unreadable and
      // show a warning dialog, unless running without UI.
      is_source_readable_ = false;
      ShowWarningDialog();
    }
  }
}

void ImporterHost::CheckForLoadedModels(uint16 items) {
  // A target profile must be loaded by StartImportSettings().
  DCHECK(profile_);

  // BookmarkModel should be loaded before adding IE favorites. So we observe
  // the BookmarkModel if needed, and start the task after it has been loaded.
  if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
    profile_->GetBookmarkModel()->AddObserver(this);
    waiting_for_bookmarkbar_model_ = true;
    installed_bookmark_observer_ = true;
  }

  // Observes the TemplateURLService if needed to import search engines from the
  // other browser. We also check to see if we're importing bookmarks because
  // we can import bookmark keywords from Firefox as search engines.
  if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) {
    if (!writer_->TemplateURLServiceIsLoaded()) {
      TemplateURLService* model =
          TemplateURLServiceFactory::GetForProfile(profile_);
      registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
                     content::Source<TemplateURLService>(model));
      model->Load();
    }
  }
}

void ImporterHost::InvokeTaskIfDone() {
  if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() ||
      !is_source_readable_)
    return;
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task_);
}

void ImporterHost::Loaded(BookmarkModel* model, bool ids_reassigned) {
  DCHECK(model->IsLoaded());
  model->RemoveObserver(this);
  waiting_for_bookmarkbar_model_ = false;
  installed_bookmark_observer_ = false;

  InvokeTaskIfDone();
}

void ImporterHost::BookmarkModelBeingDeleted(BookmarkModel* model) {
  installed_bookmark_observer_ = false;
}

void ImporterHost::BookmarkModelChanged() {
}

void ImporterHost::Observe(int type,
                           const content::NotificationSource& source,
                           const content::NotificationDetails& details) {
  DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED);
  registrar_.RemoveAll();
  InvokeTaskIfDone();
}
<commit_msg>Ensure that the GAIA signin forced when importing from the Google Toolbar happens in the browser with the correct profile.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/importer/importer_host.h"

#include "base/bind.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/importer/firefox_profile_lock.h"
#include "chrome/browser/importer/importer.h"
#include "chrome/browser/importer/importer_lock_dialog.h"
#include "chrome/browser/importer/importer_progress_observer.h"
#include "chrome/browser/importer/importer_type.h"
#include "chrome/browser/importer/in_process_importer_bridge.h"
#include "chrome/browser/importer/toolbar_importer_utils.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/public/browser/notification_source.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

#if defined(OS_WIN)
// TODO(port): Port this file.
#include "ui/base/message_box_win.h"
#endif

ImporterHost::ImporterHost()
    : profile_(NULL),
      waiting_for_bookmarkbar_model_(false),
      installed_bookmark_observer_(false),
      is_source_readable_(true),
      importer_(NULL),
      headless_(false),
      parent_window_(NULL),
      observer_(NULL) {
}

void ImporterHost::ShowWarningDialog() {
  if (headless_)
    OnImportLockDialogEnd(false);
  else
    importer::ShowImportLockDialog(parent_window_, this);
}

void ImporterHost::OnImportLockDialogEnd(bool is_continue) {
  if (is_continue) {
    // User chose to continue, then we check the lock again to make
    // sure that Firefox has been closed. Try to import the settings
    // if successful. Otherwise, show a warning dialog.
    firefox_lock_->Lock();
    if (firefox_lock_->HasAcquired()) {
      is_source_readable_ = true;
      InvokeTaskIfDone();
    } else {
      ShowWarningDialog();
    }
  } else {
    // User chose to skip the import process. We should reset the |task_| and
    // notify the ImporterHost to finish.
    task_ = base::Closure();
    importer_ = NULL;
    NotifyImportEnded();
  }
}

void ImporterHost::SetObserver(importer::ImporterProgressObserver* observer) {
  observer_ = observer;
}

void ImporterHost::NotifyImportStarted() {
  if (observer_)
    observer_->ImportStarted();
}

void ImporterHost::NotifyImportItemStarted(importer::ImportItem item) {
  if (observer_)
    observer_->ImportItemStarted(item);
}

void ImporterHost::NotifyImportItemEnded(importer::ImportItem item) {
  if (observer_)
    observer_->ImportItemEnded(item);
}

void ImporterHost::NotifyImportEnded() {
  firefox_lock_.reset();  // Release the Firefox profile lock.
  if (observer_)
    observer_->ImportEnded();
  Release();
}

void ImporterHost::StartImportSettings(
    const importer::SourceProfile& source_profile,
    Profile* target_profile,
    uint16 items,
    ProfileWriter* writer,
    bool first_run) {
  // We really only support importing from one host at a time.
  DCHECK(!profile_);
  DCHECK(target_profile);

  profile_ = target_profile;
  PrefService* user_prefs = profile_->GetPrefs();

  // Make sure only items that were not disabled by policy are imported.
  if (!user_prefs->GetBoolean(prefs::kImportHistory))
    items &= ~importer::HISTORY;
  if (!user_prefs->GetBoolean(prefs::kImportSearchEngine))
    items &= ~importer::SEARCH_ENGINES;
  if (!user_prefs->GetBoolean(prefs::kImportBookmarks))
    items &= ~importer::FAVORITES;
  if (!user_prefs->GetBoolean(prefs::kImportSavedPasswords))
    items &= ~importer::PASSWORDS;

  // Preserves the observer and creates a task, since we do async import so that
  // it doesn't block the UI. When the import is complete, observer will be
  // notified.
  writer_ = writer;
  importer_ = importer::CreateImporterByType(source_profile.importer_type);
  // If we fail to create the Importer, exit, as we cannot do anything.
  if (!importer_) {
    NotifyImportEnded();
    return;
  }

  importer_->AddRef();

  scoped_refptr<InProcessImporterBridge> bridge(
      new InProcessImporterBridge(writer_.get(), this));
  task_ = base::Bind(
      &Importer::StartImport, importer_, source_profile, items, bridge);

  CheckForFirefoxLock(source_profile, items, first_run);

#if defined(OS_WIN)
  // For google toolbar import, we need the user to log in and store their GAIA
  // credentials.
  if (source_profile.importer_type == importer::TYPE_GOOGLE_TOOLBAR5) {
    toolbar_importer_utils::IsGoogleGAIACookieInstalled(
        base::Bind(&ImporterHost::OnGoogleGAIACookieChecked, this), profile_);
    is_source_readable_ = false;
  }
#endif

  CheckForLoadedModels(items);
  AddRef();
  InvokeTaskIfDone();
}

void ImporterHost::OnGoogleGAIACookieChecked(bool result) {
#if defined(OS_WIN)
  if (!result) {
    ui::MessageBox(
        NULL,
        UTF16ToWide(l10n_util::GetStringUTF16(
            IDS_IMPORTER_GOOGLE_LOGIN_TEXT)).c_str(),
        L"",
        MB_OK | MB_TOPMOST);

    GURL url("https://www.google.com/accounts/ServiceLogin");
    DCHECK(profile_);
    Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
    if (browser)
      browser->AddSelectedTabWithURL(url, content::PAGE_TRANSITION_TYPED);

    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
        &ImporterHost::OnImportLockDialogEnd, this, false));
  } else {
    is_source_readable_ = true;
    InvokeTaskIfDone();
  }
#endif
}

void ImporterHost::Cancel() {
  if (importer_)
    importer_->Cancel();
}

ImporterHost::~ImporterHost() {
  if (NULL != importer_)
    importer_->Release();

  if (installed_bookmark_observer_) {
    DCHECK(profile_);
    profile_->GetBookmarkModel()->RemoveObserver(this);
  }
}

void ImporterHost::CheckForFirefoxLock(
    const importer::SourceProfile& source_profile,
    uint16 items,
    bool first_run) {
  if (source_profile.importer_type == importer::TYPE_FIREFOX2 ||
      source_profile.importer_type == importer::TYPE_FIREFOX3) {
    DCHECK(!firefox_lock_.get());
    firefox_lock_.reset(new FirefoxProfileLock(source_profile.source_path));
    if (!firefox_lock_->HasAcquired()) {
      // If fail to acquire the lock, we set the source unreadable and
      // show a warning dialog, unless running without UI.
      is_source_readable_ = false;
      ShowWarningDialog();
    }
  }
}

void ImporterHost::CheckForLoadedModels(uint16 items) {
  // A target profile must be loaded by StartImportSettings().
  DCHECK(profile_);

  // BookmarkModel should be loaded before adding IE favorites. So we observe
  // the BookmarkModel if needed, and start the task after it has been loaded.
  if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
    profile_->GetBookmarkModel()->AddObserver(this);
    waiting_for_bookmarkbar_model_ = true;
    installed_bookmark_observer_ = true;
  }

  // Observes the TemplateURLService if needed to import search engines from the
  // other browser. We also check to see if we're importing bookmarks because
  // we can import bookmark keywords from Firefox as search engines.
  if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) {
    if (!writer_->TemplateURLServiceIsLoaded()) {
      TemplateURLService* model =
          TemplateURLServiceFactory::GetForProfile(profile_);
      registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
                     content::Source<TemplateURLService>(model));
      model->Load();
    }
  }
}

void ImporterHost::InvokeTaskIfDone() {
  if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() ||
      !is_source_readable_)
    return;
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task_);
}

void ImporterHost::Loaded(BookmarkModel* model, bool ids_reassigned) {
  DCHECK(model->IsLoaded());
  model->RemoveObserver(this);
  waiting_for_bookmarkbar_model_ = false;
  installed_bookmark_observer_ = false;

  InvokeTaskIfDone();
}

void ImporterHost::BookmarkModelBeingDeleted(BookmarkModel* model) {
  installed_bookmark_observer_ = false;
}

void ImporterHost::BookmarkModelChanged() {
}

void ImporterHost::Observe(int type,
                           const content::NotificationSource& source,
                           const content::NotificationDetails& details) {
  DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED);
  registrar_.RemoveAll();
  InvokeTaskIfDone();
}
<|endoftext|>


<commit_before>#include "test/Test.h"
#include <cstdio>

int main()
{
	Penciloid::PenciloidTest::RunTestAll();

	return 0;
}<commit_msg>Add performance test switch<commit_after>#include "test/Test.h"
#include <cstdio>

int main()
{
#ifdef PL_PERFORMANCE_TEST
	Penciloid::PenciloidTest::ConsecutivePerformanceTest("perf_test.txt");
#else
	Penciloid::PenciloidTest::RunTestAll();
#endif

	return 0;
}<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * 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 "modules/perception/fusion/base/sensor_data_manager.h"

#include <utility>

#include "cyber/common/log.h"

namespace apollo {
namespace perception {
namespace fusion {

SensorDataManager::SensorDataManager() { CHECK_EQ(this->Init(), true); }

bool SensorDataManager::Init() {
  if (inited_) {
    return true;
  }
  sensor_manager_ = common::SensorManager::Instance();
  inited_ = true;
  return true;
}

void SensorDataManager::Reset() {
  inited_ = false;
  sensor_manager_ = nullptr;
  sensors_.clear();
}

void SensorDataManager::AddSensorMeasurements(
    const base::FrameConstPtr& frame_ptr) {
  const base::SensorInfo sensor_info = frame_ptr->sensor_info;
  std::string sensor_id = sensor_info.name;
  const auto& it = sensors_.find(sensor_id);
  SensorPtr sensor_ptr = nullptr;
  if (it == sensors_.end()) {
    if (!sensor_manager_->IsSensorExist(sensor_id)) {
      AERROR << "Failed to find sensor " << sensor_id << " in sensor manager.";
      return;
    }
    sensor_ptr = std::make_shared<Sensor>(Sensor(sensor_info));
    sensors_.insert(make_pair(sensor_id, sensor_ptr));
  } else {
    sensor_ptr = it->second;
  }

  sensor_ptr->AddFrame(frame_ptr);
}

bool SensorDataManager::IsLidar(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsLidar(type);
}

bool SensorDataManager::IsRadar(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsRadar(type);
}

bool SensorDataManager::IsCamera(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsCamera(type);
}

void SensorDataManager::GetLatestSensorFrames(
    double timestamp, const std::string& sensor_id,
    std::vector<SensorFramePtr>* frames) const {
  if (frames == nullptr) {
    AERROR << "Nullptr error.";
    return;
  }
  const auto& it = sensors_.find(sensor_id);
  if (it == sensors_.end()) {
    return;
  }
  return it->second->QueryLatestFrames(timestamp, frames);
}

void SensorDataManager::GetLatestFrames(
    double timestamp, std::vector<SensorFramePtr>* frames) const {
  if (frames == nullptr) {
    AERROR << "Nullptr error.";
    return;
  }

  frames->clear();
  for (auto it = sensors_.begin(); it != sensors_.end(); ++it) {
    SensorFramePtr frame = it->second->QueryLatestFrame(timestamp);
    if (frame != nullptr) {
      frames->push_back(frame);
    }
  }

  if (frames->empty()) {
    return;
  }

  for (size_t i = 0; i < frames->size() - 1; ++i) {
    for (size_t j = i + 1; j < frames->size(); ++j) {
      if ((*frames)[j]->GetTimestamp() < (*frames)[i]->GetTimestamp()) {
        std::swap((*frames)[j], (*frames)[i]);
      }
    }
  }
}

bool SensorDataManager::GetPose(const std::string& sensor_id, double timestamp,
                                Eigen::Affine3d* pose) const {
  if (pose == nullptr) {
    AERROR << "Nullptr error.";
    return false;
  }

  const auto& it = sensors_.find(sensor_id);
  if (it == sensors_.end()) {
    AERROR << "Failed to find sensor " << sensor_id << " for get pose.";
    return false;
  }

  return it->second->GetPose(timestamp, pose);
}

base::BaseCameraModelPtr SensorDataManager::GetCameraIntrinsic(
    const std::string& sensor_id) const {
  return sensor_manager_->GetUndistortCameraModel(sensor_id);
}

}  // namespace fusion
}  // namespace perception
}  // namespace apollo
<commit_msg>Perception: Simple code clean.<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * 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 "modules/perception/fusion/base/sensor_data_manager.h"

#include <utility>

#include "cyber/common/log.h"

namespace apollo {
namespace perception {
namespace fusion {

SensorDataManager::SensorDataManager() { CHECK_EQ(this->Init(), true); }

bool SensorDataManager::Init() {
  if (inited_) {
    return true;
  }
  sensor_manager_ = common::SensorManager::Instance();
  inited_ = true;
  return true;
}

void SensorDataManager::Reset() {
  inited_ = false;
  sensor_manager_ = nullptr;
  sensors_.clear();
}

void SensorDataManager::AddSensorMeasurements(
    const base::FrameConstPtr& frame_ptr) {
  const base::SensorInfo& sensor_info = frame_ptr->sensor_info;
  std::string sensor_id = sensor_info.name;
  const auto it = sensors_.find(sensor_id);
  SensorPtr sensor_ptr = nullptr;
  if (it == sensors_.end()) {
    if (!sensor_manager_->IsSensorExist(sensor_id)) {
      AERROR << "Failed to find sensor " << sensor_id << " in sensor manager.";
      return;
    }
    sensor_ptr = std::make_shared<Sensor>(Sensor(sensor_info));
    sensors_.emplace(sensor_id, sensor_ptr);
  } else {
    sensor_ptr = it->second;
  }

  sensor_ptr->AddFrame(frame_ptr);
}

bool SensorDataManager::IsLidar(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsLidar(type);
}

bool SensorDataManager::IsRadar(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsRadar(type);
}

bool SensorDataManager::IsCamera(const base::FrameConstPtr& frame_ptr) {
  base::SensorType type = frame_ptr->sensor_info.type;
  return sensor_manager_->IsCamera(type);
}

void SensorDataManager::GetLatestSensorFrames(
    double timestamp, const std::string& sensor_id,
    std::vector<SensorFramePtr>* frames) const {
  if (frames == nullptr) {
    AERROR << "Nullptr error.";
    return;
  }
  const auto it = sensors_.find(sensor_id);
  if (it == sensors_.end()) {
    return;
  }
  return it->second->QueryLatestFrames(timestamp, frames);
}

void SensorDataManager::GetLatestFrames(
    double timestamp, std::vector<SensorFramePtr>* frames) const {
  if (frames == nullptr) {
    AERROR << "Nullptr error.";
    return;
  }

  frames->clear();
  for (auto it = sensors_.begin(); it != sensors_.end(); ++it) {
    SensorFramePtr frame = it->second->QueryLatestFrame(timestamp);
    if (frame != nullptr) {
      frames->push_back(frame);
    }
  }

  if (frames->empty()) {
    return;
  }

  for (size_t i = 0; i < frames->size() - 1; ++i) {
    for (size_t j = i + 1; j < frames->size(); ++j) {
      if ((*frames)[j]->GetTimestamp() < (*frames)[i]->GetTimestamp()) {
        std::swap((*frames)[j], (*frames)[i]);
      }
    }
  }
}

bool SensorDataManager::GetPose(const std::string& sensor_id, double timestamp,
                                Eigen::Affine3d* pose) const {
  if (pose == nullptr) {
    AERROR << "Nullptr error.";
    return false;
  }

  const auto it = sensors_.find(sensor_id);
  if (it == sensors_.end()) {
    AERROR << "Failed to find sensor " << sensor_id << " for get pose.";
    return false;
  }

  return it->second->GetPose(timestamp, pose);
}

base::BaseCameraModelPtr SensorDataManager::GetCameraIntrinsic(
    const std::string& sensor_id) const {
  return sensor_manager_->GetUndistortCameraModel(sensor_id);
}

}  // namespace fusion
}  // namespace perception
}  // namespace apollo
<|endoftext|>


<commit_before>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "PolygonTest.h"

namespace cura
{
    CPPUNIT_TEST_SUITE_REGISTRATION(PolygonTest);

void PolygonTest::setUp()
{
    test_square.emplace_back(0, 0);
    test_square.emplace_back(100, 0);
    test_square.emplace_back(100, 100);
    test_square.emplace_back(0, 100);

    pointy_square.emplace_back(0, 0);
    pointy_square.emplace_back(47, 0);
    pointy_square.emplace_back(50, 80);
    pointy_square.emplace_back(53, 0);
    pointy_square.emplace_back(100, 0);
    pointy_square.emplace_back(100, 100);
    pointy_square.emplace_back(55, 100);
    pointy_square.emplace_back(50, 180);
    pointy_square.emplace_back(45, 100);
    pointy_square.emplace_back(0, 100);

    triangle.emplace_back(100, 0);
    triangle.emplace_back(300, 0);
    triangle.emplace_back(200, 100);

    clipper_bug.emplace_back(107347, 120836);
    clipper_bug.emplace_back(107309, 120910);
    clipper_bug.emplace_back(107158, 120960);
    clipper_bug.emplace_back(106760, 120839);
    clipper_bug.emplace_back(106570, 120831);

    clockwise_large.emplace_back(-100, -100);
    clockwise_large.emplace_back(-100, 100);
    clockwise_large.emplace_back(100, 100);
    clockwise_large.emplace_back(100, -100);

    clockwise_small.emplace_back(-50, -50);
    clockwise_small.emplace_back(-50, 50);
    clockwise_small.emplace_back(50, 50);
    clockwise_small.emplace_back(50, -50);

    Polygons outer, inner;
    outer.add(clockwise_large);
    inner.add(clockwise_small);
    clockwise_donut = outer.difference(inner);
}

void PolygonTest::tearDown()
{
    //Do nothing.
}

void PolygonTest::polygonOffsetTest()
{
    Polygons test_squares;
    test_squares.add(test_square);
    Polygons expanded = test_squares.offset(25);
    int64_t expanded_length = expanded.polygonLength();

    Polygons square_hole;
    PolygonRef square_inverted = square_hole.newPoly();
    for (int i = test_square.size() - 1; i >= 0; i--)
    {
        square_inverted.add(test_square[i]);
    }
    Polygons contracted = square_hole.offset(25);
    int64_t contracted_length = contracted.polygonLength();

    CPPUNIT_ASSERT_MESSAGE("Offset on outside poly is different from offset on inverted poly!", std::abs(expanded_length - contracted_length) < 5);
}

void PolygonTest::polygonOffsetBugTest()
{
    Polygons polys;
    polys.add(clipper_bug);
    Polygons offsetted = polys.offset(-20);

    for (PolygonRef poly : offsetted)
    {
        for (Point& p : poly)
        {
            CPPUNIT_ASSERT_MESSAGE("Polygon offset moved point the wrong way!", polys.inside(p));
        }
    }
}

void PolygonTest::isOutsideTest()
{
    Polygons test_triangle;
    test_triangle.add(triangle);

    CPPUNIT_ASSERT_MESSAGE("Left point is calculated as inside while it's outside!", !test_triangle.inside(Point(0, 100)));
    CPPUNIT_ASSERT_MESSAGE("Middle left point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, 100)));
    CPPUNIT_ASSERT_MESSAGE("Middle right point is calculated as inside while it's outside!", !test_triangle.inside(Point(300, 100)));
    CPPUNIT_ASSERT_MESSAGE("Right point is calculated as inside while it's outside!", !test_triangle.inside(Point(500, 100)));
    CPPUNIT_ASSERT_MESSAGE("Above point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, 200)));
    CPPUNIT_ASSERT_MESSAGE("Below point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, -100)));
}

void PolygonTest::isInsideTest()
{
    Polygons test_polys;
    PolygonRef poly = test_polys.newPoly();
    poly.add(Point(82124,98235));
    poly.add(Point(83179,98691));
    poly.add(Point(83434,98950));
    poly.add(Point(82751,99026));
    poly.add(Point(82528,99019));
    poly.add(Point(81605,98854));
    poly.add(Point(80401,98686));
    poly.add(Point(79191,98595));
    poly.add(Point(78191,98441));
    poly.add(Point(78998,98299));
    poly.add(Point(79747,98179));
    poly.add(Point(80960,98095));

    CPPUNIT_ASSERT_MESSAGE("Inside point is calculated as being outside!", test_polys.inside(Point(78315, 98440)));

}

void PolygonTest::splitIntoPartsWithHoleTest()
{
    const std::vector<PolygonsPart> parts = clockwise_donut.splitIntoParts();

    CPPUNIT_ASSERT_MESSAGE("difference between two polygons is not one PolygonsPart!", parts.size() == 1);
}

void PolygonTest::differenceContainsOriginalPointTest()
{
    PolygonsPart part = clockwise_donut.splitIntoParts()[0];
    PolygonRef outer = part.outerPolygon();
    CPPUNIT_ASSERT_MESSAGE("Outer vertex cannot be found in polygons difference!", std::find(outer.begin(), outer.end(), clockwise_large[0]) != outer.end());
    PolygonRef inner = part[1];
    CPPUNIT_ASSERT_MESSAGE("Inner vertex cannot be found in polygons difference!", std::find(inner.begin(), inner.end(), clockwise_small[0]) != inner.end());
}

void PolygonTest::differenceClockwiseTest()
{
    PolygonsPart part = clockwise_donut.splitIntoParts()[0];

    PolygonRef outer = part.outerPolygon();
    //Apply the shoelace formula to determine surface area. If it's negative, the polygon is counterclockwise.
    coord_t area = 0;
    for (size_t point_index = 0; point_index < outer.size(); point_index++)
    {
        const size_t next_index = (point_index + 1) % outer.size();
        const Point point = outer[point_index];
        const Point next = outer[next_index];
        area += (next.X - point.X) * (point.Y + next.Y);
    }
    CPPUNIT_ASSERT_MESSAGE("Outer polygon is not counter-clockwise!", area < 0);

    PolygonRef inner = part[1];
    area = 0;
    for (size_t point_index = 0; point_index < inner.size(); point_index++)
    {
        const size_t next_index = (point_index + 1) % inner.size();
        const Point point = inner[point_index];
        const Point next = inner[next_index];
        area += (next.X - point.X) * (point.Y + next.Y);
    }
    CPPUNIT_ASSERT_MESSAGE("Inner polygon is not clockwise!", area > 0);
}

void PolygonTest::getEmptyHolesTest()
{
    Polygons holes = clockwise_donut.getEmptyHoles();

    CPPUNIT_ASSERT_MESSAGE("Not the correct number of holes!", holes.size() == 1);
    CPPUNIT_ASSERT_MESSAGE("Empty hole doesn't have the correct amount of vertices!", holes[0].size() == clockwise_small.size());
    for (size_t point_index = 0; point_index < holes[0].size(); point_index++)
    {
        CPPUNIT_ASSERT_MESSAGE("Coordinates of empty hole are wrong!", holes[0][point_index] == clockwise_small[point_index]);
    }
}

void PolygonTest::simplifyCircle()
{
    Polygons circle_polygons;
    PolygonRef circle = circle_polygons.newPoly();
    constexpr coord_t radius = 100000;
    constexpr double segment_length = 1000;
    constexpr double tau = 6.283185307179586476925286766559; //2 * pi.
    constexpr double increment = segment_length / radius; //Segments of 1000 units.
    for (double angle = 0; angle < tau; angle += increment)
    {
        circle.add(Point(std::cos(angle) * radius, std::sin(angle) * radius));
    }

    constexpr coord_t minimum_segment_length = segment_length + 10;
    circle_polygons.simplify(minimum_segment_length, 999999999); //With segments of 1000, we need to remove exactly half of the vertices to meet the requirement that all segments are >1010.
    constexpr coord_t maximum_segment_length = segment_length * 2 + 20; //+20 for some error margin due to rounding.

    for (size_t point_index = 1; point_index < circle.size() - 1; point_index++) //Don't check the last vertex. Due to odd-numbered vertices it has to be shorter than the minimum.
    {
        coord_t segment_length = vSize(circle[point_index % circle.size()] - circle[point_index - 1]);
        std::stringstream ss_short;
        ss_short << "Segment " << (point_index - 1) << " - " << point_index << " is too short! " << segment_length << " < " << minimum_segment_length;
        CPPUNIT_ASSERT_MESSAGE(ss_short.str(), segment_length >= minimum_segment_length);
        std::stringstream ss_long;
        ss_long << "Segment " << (point_index - 1) << " - " << point_index << " is too long! " << segment_length << " > " << maximum_segment_length;
        CPPUNIT_ASSERT_MESSAGE(ss_long.str(), segment_length <= maximum_segment_length);
    }
}

void PolygonTest::simplifyZigzag()
{
    //Tests a zigzag line: /\/\/\/\/\/\/
    //If all line segments are short, they can all be removed and turned into one long line: -------------------
    Polygons zigzag_polygons;
    PolygonRef zigzag = zigzag_polygons.newPoly();
    constexpr coord_t segment_length = 1000;
    constexpr coord_t y_increment = segment_length / sqrt(2);
    coord_t x = y_increment / 2;

    for (size_t i = 0; i < 100; i++)
    {
        zigzag.add(Point(x, i * y_increment));
        x = 0 - x;
    }

    constexpr coord_t maximum_error = 2 * segment_length * segment_length + 100; //Squared offset from baseline (and some margin for rounding).
    zigzag_polygons.simplify(segment_length + 10, maximum_error);

    std::stringstream ss;
    ss << "Zigzag should be removed since the total error compensates with each zag, but size was " << zigzag.size() << ".";
    CPPUNIT_ASSERT_MESSAGE(ss.str(), zigzag.size() <= 5);
}

void PolygonTest::simplifyLimitedLength()
{
    //Generate a spiral with segments that gradually increase in length.
    Polygons spiral_polygons;
    PolygonRef spiral = spiral_polygons.newPoly();
    spiral.add(Point());

    coord_t segment_length = 1000;
    double angle = 0;
    Point last_position;

    for (size_t i = 0; i < 10; i++)
    {
        const coord_t dx = std::cos(angle) * segment_length;
        const coord_t dy = std::sin(angle) * segment_length;
        last_position += Point(dx, dy);
        spiral.add(last_position);
        segment_length += 100;
        angle += 0.1;
    }

    spiral_polygons.simplify(1550, 999999999); //Remove segments smaller than 1550 (infinite area error).

    CPPUNIT_ASSERT_MESSAGE(std::string("Should merge segments of length 1100 with 1200, 1300 with 1400 and first with last."), spiral.size() == 11 - 3);
}

void PolygonTest::simplifyLimitedError()
{
    //Generate a square spiral with increasingly large corners until the area exceeds the limit.
    Polygons spiral_polygons;
    PolygonRef spiral = spiral_polygons.newPoly();
    spiral.add(Point());

    //Generate a square spiral, 90 degree corners to make it easy to compute the area loss while retaining a positive area per corner.
    coord_t segment_length = 1000;
    Point last_position;
    double angle = 0;
    for (size_t i = 0; i < 10; i++)
    {
        const coord_t dx = std::cos(angle) * segment_length;
        const coord_t dy = std::sin(angle) * segment_length;
        last_position += Point(dx, dy);
        spiral.add(last_position);
        segment_length += 100;
        angle += M_PI / 2;
    }

    //We want it to not merge the lines 1400 and 1500 any more, but do merge all lines before it.
    //Take the area of the 1400 by 1500 and plug it into the formula for the height to get at the baseline height, which is our allowed error.
    constexpr coord_t area = 1400 * 1500 / 2;
    constexpr coord_t diagonal_length = std::sqrt(1400 * 1400 + 1500 * 1500); //Pythagoras.
    //A = 0.5 * b * h. diagonal_length is the base line in this case.
    //2A = b * h
    //2A / b = h
    constexpr coord_t height = 4 * area / diagonal_length; //Error of the first vertex we want to keep, so we must set the limit to something slightly lower than this.
    spiral_polygons.simplify(999999999, height - 10);

    CPPUNIT_ASSERT_MESSAGE(std::string("Should merge segments of length 1000 through 1400 and first with last."), spiral.size() == 11 - 5);
}

}
<commit_msg>PolygonTest: std::sqrt is not constexpr<commit_after>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "PolygonTest.h"

namespace cura
{
    CPPUNIT_TEST_SUITE_REGISTRATION(PolygonTest);

void PolygonTest::setUp()
{
    test_square.emplace_back(0, 0);
    test_square.emplace_back(100, 0);
    test_square.emplace_back(100, 100);
    test_square.emplace_back(0, 100);

    pointy_square.emplace_back(0, 0);
    pointy_square.emplace_back(47, 0);
    pointy_square.emplace_back(50, 80);
    pointy_square.emplace_back(53, 0);
    pointy_square.emplace_back(100, 0);
    pointy_square.emplace_back(100, 100);
    pointy_square.emplace_back(55, 100);
    pointy_square.emplace_back(50, 180);
    pointy_square.emplace_back(45, 100);
    pointy_square.emplace_back(0, 100);

    triangle.emplace_back(100, 0);
    triangle.emplace_back(300, 0);
    triangle.emplace_back(200, 100);

    clipper_bug.emplace_back(107347, 120836);
    clipper_bug.emplace_back(107309, 120910);
    clipper_bug.emplace_back(107158, 120960);
    clipper_bug.emplace_back(106760, 120839);
    clipper_bug.emplace_back(106570, 120831);

    clockwise_large.emplace_back(-100, -100);
    clockwise_large.emplace_back(-100, 100);
    clockwise_large.emplace_back(100, 100);
    clockwise_large.emplace_back(100, -100);

    clockwise_small.emplace_back(-50, -50);
    clockwise_small.emplace_back(-50, 50);
    clockwise_small.emplace_back(50, 50);
    clockwise_small.emplace_back(50, -50);

    Polygons outer, inner;
    outer.add(clockwise_large);
    inner.add(clockwise_small);
    clockwise_donut = outer.difference(inner);
}

void PolygonTest::tearDown()
{
    //Do nothing.
}

void PolygonTest::polygonOffsetTest()
{
    Polygons test_squares;
    test_squares.add(test_square);
    Polygons expanded = test_squares.offset(25);
    int64_t expanded_length = expanded.polygonLength();

    Polygons square_hole;
    PolygonRef square_inverted = square_hole.newPoly();
    for (int i = test_square.size() - 1; i >= 0; i--)
    {
        square_inverted.add(test_square[i]);
    }
    Polygons contracted = square_hole.offset(25);
    int64_t contracted_length = contracted.polygonLength();

    CPPUNIT_ASSERT_MESSAGE("Offset on outside poly is different from offset on inverted poly!", std::abs(expanded_length - contracted_length) < 5);
}

void PolygonTest::polygonOffsetBugTest()
{
    Polygons polys;
    polys.add(clipper_bug);
    Polygons offsetted = polys.offset(-20);

    for (PolygonRef poly : offsetted)
    {
        for (Point& p : poly)
        {
            CPPUNIT_ASSERT_MESSAGE("Polygon offset moved point the wrong way!", polys.inside(p));
        }
    }
}

void PolygonTest::isOutsideTest()
{
    Polygons test_triangle;
    test_triangle.add(triangle);

    CPPUNIT_ASSERT_MESSAGE("Left point is calculated as inside while it's outside!", !test_triangle.inside(Point(0, 100)));
    CPPUNIT_ASSERT_MESSAGE("Middle left point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, 100)));
    CPPUNIT_ASSERT_MESSAGE("Middle right point is calculated as inside while it's outside!", !test_triangle.inside(Point(300, 100)));
    CPPUNIT_ASSERT_MESSAGE("Right point is calculated as inside while it's outside!", !test_triangle.inside(Point(500, 100)));
    CPPUNIT_ASSERT_MESSAGE("Above point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, 200)));
    CPPUNIT_ASSERT_MESSAGE("Below point is calculated as inside while it's outside!", !test_triangle.inside(Point(100, -100)));
}

void PolygonTest::isInsideTest()
{
    Polygons test_polys;
    PolygonRef poly = test_polys.newPoly();
    poly.add(Point(82124,98235));
    poly.add(Point(83179,98691));
    poly.add(Point(83434,98950));
    poly.add(Point(82751,99026));
    poly.add(Point(82528,99019));
    poly.add(Point(81605,98854));
    poly.add(Point(80401,98686));
    poly.add(Point(79191,98595));
    poly.add(Point(78191,98441));
    poly.add(Point(78998,98299));
    poly.add(Point(79747,98179));
    poly.add(Point(80960,98095));

    CPPUNIT_ASSERT_MESSAGE("Inside point is calculated as being outside!", test_polys.inside(Point(78315, 98440)));

}

void PolygonTest::splitIntoPartsWithHoleTest()
{
    const std::vector<PolygonsPart> parts = clockwise_donut.splitIntoParts();

    CPPUNIT_ASSERT_MESSAGE("difference between two polygons is not one PolygonsPart!", parts.size() == 1);
}

void PolygonTest::differenceContainsOriginalPointTest()
{
    PolygonsPart part = clockwise_donut.splitIntoParts()[0];
    PolygonRef outer = part.outerPolygon();
    CPPUNIT_ASSERT_MESSAGE("Outer vertex cannot be found in polygons difference!", std::find(outer.begin(), outer.end(), clockwise_large[0]) != outer.end());
    PolygonRef inner = part[1];
    CPPUNIT_ASSERT_MESSAGE("Inner vertex cannot be found in polygons difference!", std::find(inner.begin(), inner.end(), clockwise_small[0]) != inner.end());
}

void PolygonTest::differenceClockwiseTest()
{
    PolygonsPart part = clockwise_donut.splitIntoParts()[0];

    PolygonRef outer = part.outerPolygon();
    //Apply the shoelace formula to determine surface area. If it's negative, the polygon is counterclockwise.
    coord_t area = 0;
    for (size_t point_index = 0; point_index < outer.size(); point_index++)
    {
        const size_t next_index = (point_index + 1) % outer.size();
        const Point point = outer[point_index];
        const Point next = outer[next_index];
        area += (next.X - point.X) * (point.Y + next.Y);
    }
    CPPUNIT_ASSERT_MESSAGE("Outer polygon is not counter-clockwise!", area < 0);

    PolygonRef inner = part[1];
    area = 0;
    for (size_t point_index = 0; point_index < inner.size(); point_index++)
    {
        const size_t next_index = (point_index + 1) % inner.size();
        const Point point = inner[point_index];
        const Point next = inner[next_index];
        area += (next.X - point.X) * (point.Y + next.Y);
    }
    CPPUNIT_ASSERT_MESSAGE("Inner polygon is not clockwise!", area > 0);
}

void PolygonTest::getEmptyHolesTest()
{
    Polygons holes = clockwise_donut.getEmptyHoles();

    CPPUNIT_ASSERT_MESSAGE("Not the correct number of holes!", holes.size() == 1);
    CPPUNIT_ASSERT_MESSAGE("Empty hole doesn't have the correct amount of vertices!", holes[0].size() == clockwise_small.size());
    for (size_t point_index = 0; point_index < holes[0].size(); point_index++)
    {
        CPPUNIT_ASSERT_MESSAGE("Coordinates of empty hole are wrong!", holes[0][point_index] == clockwise_small[point_index]);
    }
}

void PolygonTest::simplifyCircle()
{
    Polygons circle_polygons;
    PolygonRef circle = circle_polygons.newPoly();
    constexpr coord_t radius = 100000;
    constexpr double segment_length = 1000;
    constexpr double tau = 6.283185307179586476925286766559; //2 * pi.
    constexpr double increment = segment_length / radius; //Segments of 1000 units.
    for (double angle = 0; angle < tau; angle += increment)
    {
        circle.add(Point(std::cos(angle) * radius, std::sin(angle) * radius));
    }

    constexpr coord_t minimum_segment_length = segment_length + 10;
    circle_polygons.simplify(minimum_segment_length, 999999999); //With segments of 1000, we need to remove exactly half of the vertices to meet the requirement that all segments are >1010.
    constexpr coord_t maximum_segment_length = segment_length * 2 + 20; //+20 for some error margin due to rounding.

    for (size_t point_index = 1; point_index < circle.size() - 1; point_index++) //Don't check the last vertex. Due to odd-numbered vertices it has to be shorter than the minimum.
    {
        coord_t segment_length = vSize(circle[point_index % circle.size()] - circle[point_index - 1]);
        std::stringstream ss_short;
        ss_short << "Segment " << (point_index - 1) << " - " << point_index << " is too short! " << segment_length << " < " << minimum_segment_length;
        CPPUNIT_ASSERT_MESSAGE(ss_short.str(), segment_length >= minimum_segment_length);
        std::stringstream ss_long;
        ss_long << "Segment " << (point_index - 1) << " - " << point_index << " is too long! " << segment_length << " > " << maximum_segment_length;
        CPPUNIT_ASSERT_MESSAGE(ss_long.str(), segment_length <= maximum_segment_length);
    }
}

void PolygonTest::simplifyZigzag()
{
    //Tests a zigzag line: /\/\/\/\/\/\/
    //If all line segments are short, they can all be removed and turned into one long line: -------------------
    Polygons zigzag_polygons;
    PolygonRef zigzag = zigzag_polygons.newPoly();
    constexpr coord_t segment_length = 1000;
    const coord_t y_increment = segment_length / std::sqrt(2);
    coord_t x = y_increment / 2;

    for (size_t i = 0; i < 100; i++)
    {
        zigzag.add(Point(x, i * y_increment));
        x = 0 - x;
    }

    constexpr coord_t maximum_error = 2 * segment_length * segment_length + 100; //Squared offset from baseline (and some margin for rounding).
    zigzag_polygons.simplify(segment_length + 10, maximum_error);

    std::stringstream ss;
    ss << "Zigzag should be removed since the total error compensates with each zag, but size was " << zigzag.size() << ".";
    CPPUNIT_ASSERT_MESSAGE(ss.str(), zigzag.size() <= 5);
}

void PolygonTest::simplifyLimitedLength()
{
    //Generate a spiral with segments that gradually increase in length.
    Polygons spiral_polygons;
    PolygonRef spiral = spiral_polygons.newPoly();
    spiral.add(Point());

    coord_t segment_length = 1000;
    double angle = 0;
    Point last_position;

    for (size_t i = 0; i < 10; i++)
    {
        const coord_t dx = std::cos(angle) * segment_length;
        const coord_t dy = std::sin(angle) * segment_length;
        last_position += Point(dx, dy);
        spiral.add(last_position);
        segment_length += 100;
        angle += 0.1;
    }

    spiral_polygons.simplify(1550, 999999999); //Remove segments smaller than 1550 (infinite area error).

    CPPUNIT_ASSERT_MESSAGE(std::string("Should merge segments of length 1100 with 1200, 1300 with 1400 and first with last."), spiral.size() == 11 - 3);
}

void PolygonTest::simplifyLimitedError()
{
    //Generate a square spiral with increasingly large corners until the area exceeds the limit.
    Polygons spiral_polygons;
    PolygonRef spiral = spiral_polygons.newPoly();
    spiral.add(Point());

    //Generate a square spiral, 90 degree corners to make it easy to compute the area loss while retaining a positive area per corner.
    coord_t segment_length = 1000;
    Point last_position;
    double angle = 0;
    for (size_t i = 0; i < 10; i++)
    {
        const coord_t dx = std::cos(angle) * segment_length;
        const coord_t dy = std::sin(angle) * segment_length;
        last_position += Point(dx, dy);
        spiral.add(last_position);
        segment_length += 100;
        angle += M_PI / 2;
    }

    //We want it to not merge the lines 1400 and 1500 any more, but do merge all lines before it.
    //Take the area of the 1400 by 1500 and plug it into the formula for the height to get at the baseline height, which is our allowed error.
    constexpr coord_t area = 1400 * 1500 / 2;
    const coord_t diagonal_length = std::sqrt(1400 * 1400 + 1500 * 1500); //Pythagoras.
    //A = 0.5 * b * h. diagonal_length is the base line in this case.
    //2A = b * h
    //2A / b = h
    const coord_t height = 4 * area / diagonal_length; //Error of the first vertex we want to keep, so we must set the limit to something slightly lower than this.
    spiral_polygons.simplify(999999999, height - 10);

    CPPUNIT_ASSERT_MESSAGE(std::string("Should merge segments of length 1000 through 1400 and first with last."), spiral.size() == 11 - 5);
}

}
<|endoftext|>