text:
#define BOOST_PYTHON_STATIC_LIB
#include <boost/python/list.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/object_attributes.hpp>
#include "DFSValue.h"
#include "FileTools.h"

using namespace BOSS;
namespace python = boost::python;

DFSValue::DFSValue(const CombatSearchParameters p,
    const std::string & dir, const std::string & prefix, const std::string & name)
    : CombatSearch_Integral(p, dir, prefix, name)
{

}

void DFSValue::recurse(const GameState & state, int depth)
{
    m_highestValueFound = recurseReturnValue(state, depth);
    m_results.buildOrder = m_integral.getBestBuildOrder();
}

FracType DFSValue::recurseReturnValue(const GameState & state, int depth)
{
    if (timeLimitReached())
    {
        throw BOSS_COMBATSEARCH_TIMEOUT;
    }

    updateResults(state);

    FracType nodeIntegralToThisPoint = m_integral.getCurrentStackValue();
    FracType nodeIntegralValue = nodeIntegralToThisPoint;
    bool ffCalculated = false;
    bool isLeafNode = true;

    ActionSetAbilities legalActions;
    generateLegalActions(state, legalActions, m_params);
    auto actionValues = evaluateStates(state, legalActions);
    std::sort(actionValues.begin(), actionValues.end(),
        [](const ActionValue & lhs, const ActionValue & rhs) { return lhs.evaluation > rhs.evaluation; });

    for (int index = 0; index < actionValues.size(); ++index)
    {
        GameState child(state);

        auto action = actionValues[index].action;

        if (action.first.isAbility())
        {
            child.doAbility(action.first, action.second);
            m_buildOrder.add(action.first, child.getLastAbility());
        }
        else
        {
            child.doAction(action.first);
            m_buildOrder.add(action.first);
        }

        //std::cout << "action added: " << action.getName() << std::endl;
        //std::cout << "target of action added: " << actionTarget << std::endl;
        //std::cout << "frame of action added: " << child.getCurrentFrame() << std::endl;

        m_integral.update(child, m_buildOrder, m_params, m_searchTimer, true);
        isLeafNode = false;

        nodeIntegralValue = std::max(nodeIntegralValue, recurseReturnValue(child, depth + 1));

        m_buildOrder.pop_back();
        m_integral.popFinishedLastOrder(state, child);
    }

    if (m_params.getSaveStates())
    {
        state.writeToSS(m_ssStates, m_params);
        m_ssStates << "," << nodeIntegralValue - nodeIntegralToThisPoint << "\n";
        //json stateValuePair;
        //stateValuePair["State"] = state.writeToJson(m_params);
        //stateValuePair["Value"] = nodeIntegralValue - nodeIntegralToThisPoint;
        //std::vector<std::uint8_t> v_msgpack = json::to_msgpack(stateValuePair);
        //m_jStates.insert(m_jStates.end(), v_msgpack.begin(), v_msgpack.end());
        m_statesWritten++;

        if (m_statesWritten % 1000000 == 0)
        {
            FileTools::MakeDirectory(CONSTANTS::ExecutablePath + "/SavedStates");
            //std::ofstream fileStates(CONSTANTS::ExecutablePath + "/SavedStates/" + m_name + "_" + std::to_string(m_filesWritten) + ".csv", std::ofstream::out | std::ofstream::app | std::ofstream::binary);
            std::ofstream fileStates(CONSTANTS::ExecutablePath + "/SavedStates/" + m_name + ".csv", std::ofstream::out | std::ofstream::app | std::ofstream::binary);
            fileStates << m_ssStates.rdbuf();
            m_ssStates.str(std::string());
            m_ssStates.clear();
            //fileStates.write(reinterpret_cast<const char*>(m_jStates.data()), m_jStates.size());
            //m_jStates.clear();
            m_filesWritten++;
        }
    }

    if (nodeIntegralValue > highestValueThusFar)
    {
        highestValueThusFar = nodeIntegralValue;
        m_ssHighestValue << m_results.nodesExpanded << "," << highestValueThusFar << "\n";
    }

    if (isLeafNode)
    {
        m_results.leafNodesExpanded++;
    }

    //std::cout << "Value to this point: " << nodeIntegralToThisPoint << ". Total value: " << nodeIntegralValue << std::endl;
    //std::cout << nodeIntegralValue << std::endl;

    return nodeIntegralValue;
}

std::vector<ActionValue> DFSValue::evaluateStates(const GameState & state, ActionSetAbilities & legalActions)
{
    std::stringstream ss;
    std::vector<ActionValue> actionValues;

    bool printNewBest = m_params.getPrintNewBest();
    m_params.setPrintNewBest(false);
    CombatSearch_IntegralDataFinishedUnits tempIntegral(m_integral);

    for (int index = 0; index < legalActions.size(); ++index)
    {
        GameState stateCopy(state);
        auto action = legalActions[index];

        if (action.first.isAbility())
        {
            stateCopy.doAbility(action.first, action.second);
        }
        else
        {
            stateCopy.doAction(action.first);
        }

        tempIntegral.update(stateCopy, m_buildOrder, m_params, m_searchTimer, false);

        ActionValue actionValue;
        actionValue.action = action;
        actionValue.evaluation = tempIntegral.getCurrentStackValue();
        actionValues.push_back(actionValue);

        tempIntegral.popFinishedLastOrder(state, stateCopy);

        stateCopy.writeToSS(ss, m_params);
        if (index < legalActions.size() - 1)
        {
            ss << "\n";
        }
    }

    m_params.setPrintNewBest(printNewBest);

    //std::cout << legalActions.toString() << std::endl;
    //std::cout << ss.str() << std::endl;

    if (legalActions.size() > 0)
    {
        // evaluate the states. the results will be returned as a list of FracTypes
        python::object values = CONSTANTS::Predictor.attr("predict")(ss.str().c_str());

        BOSS_ASSERT(len(values) == legalActions.size(), "size of values %i does not match the size of legalActions %i", len(values), legalActions.size());

        std::cout << std::endl;
        std::cout << "current frame: " << state.getCurrentFrame() << std::endl;
        for (int i = 0; i < legalActions.size(); ++i)
        {
            std::cout << "action id: " << legalActions[i].first.getID() << std::endl;
            FracType network_pred = python::extract<FracType>(values[i]);
            actionValues[i].evaluation += network_pred;
            std::cout << legalActions[i].first.getName() << ": network: " << network_pred << ", total: " << actionValues[i].evaluation << std::endl;
        }
        std::cout << std::endl;
    }

    return actionValues;
}

ext: cpp


text:
/// \file GraphModule.cpp

#include "chi/GraphModule.hpp"
#include "chi/CCompiler.hpp"
#include "chi/ClangFinder.hpp"
#include "chi/Context.hpp"
#include "chi/FunctionCompiler.hpp"
#include "chi/GraphFunction.hpp"
#include "chi/GraphStruct.hpp"
#include "chi/JsonDeserializer.hpp"
#include "chi/JsonSerializer.hpp"
#include "chi/LLVMVersion.hpp"
#include "chi/NameMangler.hpp"
#include "chi/NodeInstance.hpp"
#include "chi/NodeType.hpp"
#include "chi/Support/LibCLocator.hpp"
#include "chi/Support/Result.hpp"
#include "chi/Support/Subprocess.hpp"

#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/Linker/Linker.h>
#include <llvm/Support/Compiler.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/Program.h>
#include <llvm/Transforms/Utils/Cloning.h>

#include <boost/filesystem.hpp>
#include <boost/range.hpp>

#include <boost/uuid/uuid_io.hpp>

namespace fs = boost::filesystem;

namespace chi {

namespace {
/// The NodeType for calling C functions
struct CFuncNode : NodeType {
	CFuncNode(GraphModule& mod, std::string cCode, std::string functionName,
	          std::vector<std::string> extraArgs, std::vector<NamedDataType> inputs,
	          DataType output)
	    : NodeType{mod, "c-call", "call C code"},
	      mFunctionName{std::move(functionName)},
	      mCCode(std::move(cCode)),
	      mExtraArguments{std::move(extraArgs)},
	      mInputs{std::move(inputs)},
	      mOutput{std::move(output)},
	      mGraphModule{&mod} {
		setExecInputs({""});
		setExecOutputs({""});

		setDataInputs(mInputs);
		if (mOutput.valid()) { setDataOutputs({{"", mOutput}}); }
	}

	Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t /*execInputID*/,
	               const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		assert(io.size() == dataInputs().size() + dataOutputs().size() && outputBlocks.size() == 1);

		Result res;

		// compile the c code if it hasn't already been compiled
		if (llcompiledmod == nullptr) {
			auto args = mExtraArguments;

			// add -I for the .c dir
			args.push_back("-I");
			args.push_back(mGraphModule->pathToCSources().string());

			// find clang
			auto clangExe = findClang();
			if (clangExe.empty()) {
				res.addEntry("EUKN", "Failed to find clang in path", nlohmann::json::object());
				return res;
			}

			res += compileCToLLVM(clangExe, context().llvmContext(), args, mCCode, &llcompiledmod);

			if (!res) { return res; }
		}

		// create a copy of the module
		auto copymod = llvm::CloneModule(
#if LLVM_VERSION_AT_LEAST(7, 0)
			*
#endif
			llcompiledmod.get());

		// link it in

		auto parentModule = &compiler.llvmModule();

#if LLVM_VERSION_LESS_EQUAL(3, 7)
		llvm::Linker::LinkModules(parentModule, copymod
#if LLVM_VERSION_LESS_EQUAL(3, 5)
		                          ,
		                          llvm::Linker::DestroySource, nullptr
#endif
		);
#else
		llvm::Linker::linkModules(*parentModule, std::move(copymod));
#endif

		parentModule->setDataLayout("");

		auto llfunc = parentModule->getFunction(mFunctionName);
		assert(llfunc != nullptr);

		llvm::IRBuilder<> builder(&codegenInto);

		size_t ioSize = io.size();

		std::string outputName;

		// remove the return type if there is one
		if (!dataOutputs().empty()) {
			--ioSize;
			outputName = dataOutputs()[0].name;
		}

		auto callinst = builder.CreateCall(llfunc, {io.data(), ioSize}, outputName);
		callinst->setDebugLoc(nodeLocation);

		// store theoutput if there are any
		if (!dataOutputs().empty()) {
			auto stoInst = builder.CreateStore(callinst, io[dataInputs().size()]);
			stoInst->setDebugLoc(nodeLocation);
		}

		auto brInst = builder.CreateBr(outputBlocks[0]);
		brInst->setDebugLoc(nodeLocation);

		return res;
	}

	nlohmann::json toJSON() const override {
		auto j = nlohmann::json{};

		j             = nlohmann::json::object();
		j["code"]     = mCCode;
		j["function"] = mFunctionName;

		auto& jsonExtraFlags = j["extraflags"];
		jsonExtraFlags       = nlohmann::json::array();
		for (const auto& flag : mExtraArguments) { jsonExtraFlags.push_back(flag); }

		auto& jsonInputs = j["inputs"];
		jsonInputs       = nlohmann::json::array();
		for (const auto& in : mInputs) {
			jsonInputs.push_back({{in.name, in.type.qualifiedName()}});
		}

		if (mOutput.valid()) {
			j["output"] = mOutput.qualifiedName();
		} else {
			j["output"] = nlohmann::json();
		}

		return j;
	}

	std::unique_ptr<NodeType> clone() const override {
		Result res;

		return std::make_unique<CFuncNode>(*mGraphModule, mCCode, mFunctionName, mExtraArguments,
		                                   mInputs, mOutput);
	}

	std::string                mFunctionName;
	std::string                mCCode;
	std::vector<std::string>   mExtraArguments;
	std::vector<NamedDataType> mInputs;
	DataType                   mOutput;
	GraphModule*               mGraphModule;

	std::unique_ptr<llvm::Module> llcompiledmod;
};

struct GraphFuncCallType : public NodeType {
	GraphFuncCallType(GraphModule& json_module, std::string funcname, Result* resPtr)
	    : NodeType(json_module, std::move(funcname), ""), JModule(&json_module) {
		Result& res = *resPtr;

		auto* mygraph = JModule->functionFromName(name());
		setDescription(mygraph->description());

		if (mygraph == nullptr) {
			res.addEntry("EINT", "Graph doesn't exist in module",
			             {{"Module Name", JModule->fullName()}, {"Requested Name", name()}});
			return;
		}

		setDataOutputs(mygraph->dataOutputs());

		setDataInputs(mygraph->dataInputs());

		setExecInputs(mygraph->execInputs());
		setExecOutputs(mygraph->execOutputs());
	}

	Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
	               const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		Result res = {};

		llvm::IRBuilder<> builder(&codegenInto);
		builder.SetCurrentDebugLocation(nodeLocation);

		auto func = compiler.funcCompiler().llvmModule().getFunction(
		    mangleFunctionName(module().fullName(), name()));

		if (func == nullptr) {
			res.addEntry("EINT", "Could not find function in llvm module",
			             {{"Requested Function", name()}});
			return res;
		}

		// add the execInputID to the argument list
		std::vector<llvm::Value*> passingIO;
		passingIO.push_back(builder.getInt32(execInputID));

		std::copy(io.begin(), io.end(), std::back_inserter(passingIO));

		auto ret = builder.CreateCall(func, passingIO, "call_function");

		// create switch on return
		auto switchInst = builder.CreateSwitch(ret, outputBlocks[0]);  // TODO: better default

		auto id = 0ull;
		for (auto out : outputBlocks) {
			switchInst->addCase(builder.getInt32(id), out);
			++id;
		}

		return res;
	}

	nlohmann::json            toJSON() const override { return {}; }
	std::unique_ptr<NodeType> clone() const override {
		Result res = {};  // there shouldn't be an error but check anyways
		return std::make_unique<GraphFuncCallType>(*JModule, name(), &res);
	}

	GraphModule* JModule;
};

struct MakeStructNodeType : public NodeType {
	MakeStructNodeType(GraphStruct& ty) : NodeType(ty.module()), mStruct{&ty} {
		setName("_make_" + ty.name());
		setDescription("Make a " + ty.name() + " structure");
		makePure();

		// set inputs
		setDataInputs(ty.types());

		// set output to just be the struct
		setDataOutputs({{"", ty.dataType()}});
	}

	Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
	               size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
	               const std::vector<llvm::Value*>&      io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		llvm::IRBuilder<> builder{&codegenInto};
		builder.SetCurrentDebugLocation(nodeLocation);

		llvm::Value* out = io[io.size() - 1];  // output goes last
		for (auto id = 0ull; id < io.size() - 1; ++id) {
			auto ptr = builder.CreateStructGEP(
#if LLVM_VERSION_AT_LEAST(3, 7)
			    mStruct->dataType().llvmType(),
#endif
			    out, id);
			builder.CreateStore(io[id], ptr);
		}

		builder.CreateBr(outputBlocks[0]);

		return {};
	}

	nlohmann::json            toJSON() const override { return {}; }
	std::unique_ptr<NodeType> clone() const override {
		return std::make_unique<MakeStructNodeType>(*mStruct);
	}

	GraphStruct* mStruct;
};

struct BreakStructNodeType : public NodeType {
	BreakStructNodeType(GraphStruct& ty) : NodeType(ty.module()), mStruct{&ty} {
		setName("_break_" + ty.name());
		setDescription("Break a " + ty.name() + " structure");
		makePure();

		// set input to just be the struct
		setDataInputs({{"", ty.dataType()}});

		// set outputs
		setDataOutputs(ty.types());
	}

	Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
	               size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
	               const std::vector<llvm::Value*>&      io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		llvm::IRBuilder<> builder{&codegenInto};
		builder.SetCurrentDebugLocation(nodeLocation);

		// create temp struct
		auto tempStruct = builder.CreateAlloca(mStruct->dataType().llvmType());
		builder.CreateStore(io[0], tempStruct);

		for (auto id = 1ull; id < io.size(); ++id) {
			auto ptr = builder.CreateStructGEP(
#if LLVM_VERSION_AT_LEAST(3, 7)
			    nullptr,
#endif
			    tempStruct, id - 1);
			std::string s = stringifyLLVMType(ptr->getType());

			auto val = builder.CreateLoad(ptr);
			builder.CreateStore(val, io[id]);
		}

		builder.CreateBr(outputBlocks[0]);

		return {};
	}

	nlohmann::json            toJSON() const override { return {}; }
	std::unique_ptr<NodeType> clone() const override {
		return std::make_unique<BreakStructNodeType>(*mStruct);
	}

	GraphStruct* mStruct;
};

struct SetLocalNodeType : public NodeType {
	SetLocalNodeType(ChiModule& mod, NamedDataType ty) : NodeType(mod), mDataType{std::move(ty)} {
		setName("_set_" + mDataType.name);
		setDescription("Set " + mDataType.name);

		setDataInputs({{"", mDataType.type}});

		setExecInputs({""});
		setExecOutputs({""});
	}

	Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t /*execInputID*/,
	               const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		llvm::IRBuilder<> builder{&codegenInto};
		builder.SetCurrentDebugLocation(nodeLocation);

		auto value = compiler.funcCompiler().localVariable(mDataType.name);
		assert(value != nullptr);

		// set the value!
		builder.CreateStore(io[0], value);

		builder.CreateBr(outputBlocks[0]);

		return {};
	}

	nlohmann::json            toJSON() const override { return mDataType.type.qualifiedName(); }
	std::unique_ptr<NodeType> clone() const override {
		return std::make_unique<SetLocalNodeType>(module(), mDataType);
	}

	NamedDataType mDataType;
};

struct GetLocalNodeType : public NodeType {
	GetLocalNodeType(ChiModule& mod, NamedDataType ty) : NodeType(mod), mDataType{std::move(ty)} {
		setName("_get_" + mDataType.name);
		setDescription("Get " + mDataType.name);

		setDataOutputs({{"", mDataType.type}});

		makePure();
	}

	Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
	               const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
	               const std::vector<llvm::BasicBlock*>& outputBlocks) override {
		llvm::IRBuilder<> builder{&codegenInto};
		builder.SetCurrentDebugLocation(nodeLocation);

		auto value = compiler.funcCompiler().localVariable(mDataType.name);
		assert(value != nullptr);

		builder.CreateStore(builder.CreateLoad(value), io[0]);

		builder.CreateBr(outputBlocks[0]);

		return {};
	}

	nlohmann::json            toJSON() const override { return mDataType.type.qualifiedName(); }
	std::unique_ptr<NodeType> clone() const override {
		return std::make_unique<GetLocalNodeType>(module(), mDataType);
	}

	NamedDataType mDataType;
};

}  // namespace

GraphModule::GraphModule(Context& cont, boost::filesystem::path fullName,
                         const std::vector<boost::filesystem::path>& dependencies)
    : ChiModule(cont, fullName) {
	// load the dependencies from the context
	for (const auto& dep : dependencies) { addDependency(dep); }
}

std::vector<std::string> GraphModule::typeNames() const {
	std::vector<std::string> ret;
	ret.reserve(structs().size());

	for (const auto& ty : structs()) { ret.push_back(ty->name()); }

	return ret;
}

Result GraphModule::addForwardDeclarations(llvm::Module& module) const {
	// create prototypes
	for (auto& graph : mFunctions) {
		module.getOrInsertFunction(mangleFunctionName(fullName(), graph->name()),
		                           graph->functionType());
	}

	return {};
}

Result GraphModule::generateModule(llvm::Module& module) {
	Result res = {};

	// if C support was enabled, compile the C files
	if (cEnabled()) {
		fs::path cPath = pathToCSources();
		if (fs::is_directory(cPath)) {
			// compile the files
			for (auto direntry : boost::make_iterator_range(
			         fs::recursive_directory_iterator{cPath, fs::symlink_option::recurse}, {})) {
				const fs::path& CFile = direntry;

				if (!fs::is_regular_file(CFile) ||
				    !(CFile.extension() == ".c" || CFile.extension() == ".C" ||
				      CFile.extension() == ".cpp" || CFile.extension() == ".cxx" ||
				      CFile.extension() == ".c++" || CFile.extension() == ".cc")) {
					continue;
				}

				// find clang
				auto clangExe = findClang();
				if (clangExe.empty()) {
					res.addEntry("EUKN", "Failed to find clang in path", nlohmann::json::object());
					return res;
				}

				// compile it
				std::unique_ptr<llvm::Module> generatedModule;
				res += compileCToLLVM(clangExe, context().llvmContext(), {CFile.string()}, "",
				                      &generatedModule);

				if (!res) { return res; }

					// link it

#if LLVM_VERSION_LESS_EQUAL(3, 7)
				llvm::Linker::LinkModules(&module, generatedModule.get()
#if LLVM_VERSION_LESS_EQUAL(3, 5)
				                                       ,
				                          llvm::Linker::DestroySource, nullptr
#endif
				);
#else
				llvm::Linker::linkModules(module, std::move(generatedModule));
#endif
			}
		}
	}

	// debug info
	llvm::DIBuilder debugBuilder(module);

	auto compileUnit = debugBuilder.createCompileUnit(llvm::dwarf::DW_LANG_C,
#if LLVM_VERSION_LESS_EQUAL(3, 9)
	                                                  sourceFilePath().filename().string(),
	                                                  sourceFilePath().parent_path().string(),
#else
	                                                  debugBuilder.createFile(
	                                                      sourceFilePath().filename().string(),
	                                                      sourceFilePath().parent_path().string()),
#endif
	                                                  "Chigraph Compiler", false, "", 0);

	// create prototypes
	addForwardDeclarations(module);

	for (auto& graph : mFunctions) {
		res += compileFunction(*graph, &module,
#if LLVM_VERSION_LESS_EQUAL(3, 6)
		                       &
#endif
		                       compileUnit,
		                       debugBuilder);
	}

	debugBuilder.finalize();

	return res;
}

Result GraphModule::saveToDisk() const {
	Result res;

	// can't serialize without a workspace...
	if (!context().hasWorkspace()) {
		res.addEntry("EUKN", "Cannot serialize without a worksapce", {});
		return res;
	}

	auto modulePath = sourceFilePath();

	try {
		// create directories that conatain the path
		fs::create_directories(modulePath.parent_path());

	} catch (std::exception& e) {
		res.addEntry("EUKN", "Failed to create directoires in workspace",
		             {{"Module File", modulePath.string()}});
		return res;
	}

	// serialize
	nlohmann::json toFill = graphModuleToJson(*this);

	// save
	fs::ofstream ostr(modulePath);
	ostr << toFill.dump(2);

	return res;
}

GraphFunction* GraphModule::getOrCreateFunction(std::string                name,
                                                std::vector<NamedDataType> dataIns,
                                                std::vector<NamedDataType> dataOuts,
                                                std::vector<std::string>   execIns,
                                                std::vector<std::string> execOuts, bool* inserted) {
	// make sure there already isn't one by this name
	auto foundFunc = functionFromName(name);
	if (foundFunc != nullptr) {
		if (inserted != nullptr) { *inserted = false; }
		return foundFunc;
	}

	// invalidate the cache
	updateLastEditTime();

	mFunctions.push_back(std::make_unique<GraphFunction>(*this, std::move(name), std::move(dataIns),
	                                                     std::move(dataOuts), std::move(execIns),
	                                                     std::move(execOuts)));

	if (inserted != nullptr) { *inserted = true; }
	return mFunctions[mFunctions.size() - 1].get();
}

bool GraphModule::removeFunction(boost::string_view name, bool deleteReferences) {
	// invalidate the cache
	updateLastEditTime();

	auto funcPtr = functionFromName(name);

	if (funcPtr == nullptr) { return false; }

	removeFunction(*funcPtr, deleteReferences);

	return true;
}

void GraphModule::removeFunction(GraphFunction& func, bool deleteReferences) {
	// invalidate the cache
	updateLastEditTime();

	if (deleteReferences) {
		auto references = context().findInstancesOfType(fullName(), func.name());

		for (auto node : references) { node->function().removeNode(*node); }
	}

	auto iter = std::find_if(mFunctions.begin(), mFunctions.end(),
	                         [&func](auto& uPtr) { return uPtr.get() == &func; });
	if (iter == mFunctions.end()) { return; }

	mFunctions.erase(iter);
}

GraphFunction* GraphModule::functionFromName(boost::string_view name) const {
	auto iter = std::find_if(mFunctions.begin(), mFunctions.end(),
	                         [&](auto& ptr) { return ptr->name() == name; });

	if (iter != mFunctions.end()) { return iter->get(); }
	return nullptr;
}

Result GraphModule::nodeTypeFromName(boost::string_view name, const nlohmann::json& jsonData,
                                     std::unique_ptr<NodeType>* toFill) {
	Result res = {};

	// see if it's a C call
	if (cEnabled() && name == "c-call") {
		if (!jsonData.is_object()) {
			res.addEntry("WUKN", "Data for c-call must be an object", {{"Given Data", jsonData}});
		}

		std::string code;
		if (jsonData.is_object() && jsonData.find("code") != jsonData.end() &&
		    jsonData["code"].is_string()) {
			code = jsonData["code"];
		} else {
			res.addEntry(
			    "WUKN",
			    "Data for c-call must have a pair with the key of code and that the data is a "
			    "string",
			    {{"Given Data", jsonData}});
		}

		std::string function;
		if (jsonData.is_object() && jsonData.find("function") != jsonData.end() &&
		    jsonData["function"].is_string()) {
			function = jsonData["function"];
		} else {
			res.addEntry(
			    "WUKN",
			    "Data for c-call must have a pair with the key of function and that the data is a "
			    "string",
			    {{"Given Data", jsonData}});
		}

		std::vector<std::string> extraFlags;
		if (jsonData.is_object() && jsonData.find("extraflags") != jsonData.end() &&
		    jsonData["extraflags"].is_array()) {
			for (const auto& flag : jsonData["extraflags"]) {
				std::string str = flag;
				extraFlags.emplace_back(std::move(str));
			}
		} else {
			res.addEntry("WUKN", "Data for c-call must have an extraflags array",
			             {{"Given Data", jsonData}});
		}

		std::vector<NamedDataType> inputs;
		if (jsonData.is_object() && jsonData.find("inputs") != jsonData.end() &&
		    jsonData["inputs"].is_array()) {
			inputs.reserve(jsonData["inputs"].size());
			for (const auto& jsonArg : jsonData["inputs"]) {
				std::string docstring, qualifiedName;
				std::tie(docstring, qualifiedName) = parseObjectPair(jsonArg);

				std::string moduleName, typeName;
				std::tie(moduleName, typeName) = parseColonPair(qualifiedName);

				DataType ty;
				res += context().typeFromModule(moduleName, typeName, &ty);

				inputs.emplace_back(docstring, ty);
			}

		} else {
			res.addEntry("WUKN", "Data for c-call must have an inputs array",
			             {{"Given Data", jsonData}});
		}

		DataType output;
		if (jsonData.is_object() && jsonData.find("output") != jsonData.end()) {
			auto& outputJson = jsonData["output"];

			// if it's a string, there's a return type
			if (outputJson.is_string()) {
				std::string moduleName, typeName;
				std::tie(moduleName, typeName) = parseColonPair(outputJson);

				res += context().typeFromModule(moduleName, typeName, &output);
			}
			// now if it isn't null, then there was an error
			else if (!outputJson.is_null()) {
				res.addEntry("WUKN",
				             R"("output" element in c-call must be either null or a string)",
				             {{"Given Data", jsonData}});
			}
		} else {
			res.addEntry(
			    "WUKN",
			    "Data for c-call must have an output element that is either null or a string",
			    {{"Given Data", jsonData}});
		}

		*toFill = std::make_unique<CFuncNode>(*this, code, function, extraFlags, inputs, output);
		return res;

	} else if (name == "c-call") {
		res.addEntry("EUKN", "Tried to use a c-call node without c support enabled in the module",
		             {});
		return res;
	}

	auto graph = functionFromName(name);

	if (graph == nullptr) {
		// if it wasn't found, then see if it's a struct breaker or maker
		std::string nameStr{name.to_string()};
		if (nameStr.substr(0, 6) == "_make_") {
			auto str = structFromName(nameStr.substr(6));
			if (str != nullptr) {
				*toFill = std::make_unique<MakeStructNodeType>(*str);
				return res;
			}
		}
		if (nameStr.substr(0, 7) == "_break_") {
			auto str = structFromName(nameStr.substr(7));
			if (str != nullptr) {
				*toFill = std::make_unique<BreakStructNodeType>(*str);
				return res;
			}
		}
		if (nameStr.substr(0, 5) == "_get_") {
			if (jsonData.is_string()) {
				std::string module, typeName;

				std::tie(module, typeName) = parseColonPair(jsonData);

				DataType ty;
				res += context().typeFromModule(module, typeName, &ty);

				*toFill =
				    std::make_unique<GetLocalNodeType>(*this, NamedDataType{nameStr.substr(5), ty});
			} else {
				res.addEntry("EUKN", "Json data for _get_ node type isn't a string",
				             {{"Given Data", jsonData}});
			}
			return res;
		}
		if (nameStr.substr(0, 5) == "_set_") {
			if (jsonData.is_string()) {
				std::string module, typeName;

				std::tie(module, typeName) = parseColonPair(jsonData);

				DataType ty;
				res += context().typeFromModule(module, typeName, &ty);

				*toFill = std::make_unique<SetLocalNodeType>(
				    *this, NamedDataType{nameStr.substr(5), std::move(ty)});
			} else {
				res.addEntry("EUKN", "Json data for _set_ node type isn't a string",
				             {{"Given Data", jsonData}});
			}
			return res;
		}

		// if we get here than it's for sure not a thing
		res.addEntry("EUKN", "Graph not found in module",
		             {{"Module Name", fullName()}, {"Requested Graph", name.to_string()}});
		return res;
	}

	*toFill = std::make_unique<GraphFuncCallType>(*this, name.to_string(), &res);
	return res;
}

DataType GraphModule::typeFromName(boost::string_view name) {
	auto func = structFromName(name);

	if (func == nullptr) { return {}; }

	return func->dataType();
}

std::vector<std::string> GraphModule::nodeTypeNames() const {
	std::vector<std::string> ret;
	std::transform(mFunctions.begin(), mFunctions.end(), std::back_inserter(ret),
	               [](auto& gPtr) { return gPtr->name(); });

	for (const auto& str : structs()) {
		ret.push_back("_make_" + str->name());
		ret.push_back("_break_" + str->name());
	}

	if (cEnabled()) { ret.push_back("c-call"); }

	return ret;
}

boost::bimap<unsigned int, NodeInstance*> GraphModule::createLineNumberAssoc() const {
	// create a sorted list of GraphFunctions
	std::vector<NodeInstance*> nodes;
	for (const auto& f : functions()) {
		for (const auto& node : f->nodes()) {
			assert(node.second != nullptr);
			nodes.push_back(node.second.get());
		}
	}

	std::sort(nodes.begin(), nodes.end(), [](const auto& lhs, const auto& rhs) {
		return (lhs->function().name() + ":" + boost::uuids::to_string(lhs->id())) <
		       (rhs->function().name() + ":" + boost::uuids::to_string(rhs->id()));
	});

	boost::bimap<unsigned, NodeInstance*> ret;
	for (unsigned i = 0; i < nodes.size(); ++i) {
		ret.left.insert({i + 1, nodes[i]});  // + 1 because line numbers start at 1
	}

	return ret;
}

GraphStruct* GraphModule::structFromName(boost::string_view name) const {
	for (const auto& str : structs()) {
		if (str->name() == name) { return str.get(); }
	}
	return nullptr;
}

GraphStruct* GraphModule::getOrCreateStruct(std::string name, bool* inserted) {
	auto str = structFromName(name);

	if (str != nullptr) {
		if (inserted != nullptr) { *inserted = false; }
		return str;
	}
	// invalidate the cache
	updateLastEditTime();

	mStructs.push_back(std::make_unique<GraphStruct>(*this, std::move(name)));

	if (inserted != nullptr) { *inserted = true; }
	return mStructs[mStructs.size() - 1].get();
}

bool GraphModule::removeStruct(boost::string_view name) {
	// invalidate the cache
	updateLastEditTime();

	for (auto iter = structs().begin(); iter != structs().end(); ++iter) {
		if ((*iter)->name() == name) {
			mStructs.erase(iter);
			return true;
		}
	}

	// TODO: remove referencing nodes
	return false;
}

void GraphModule::removeStruct(GraphStruct& tyToDel) {
	assert(&tyToDel.module() == this);

	LLVM_ATTRIBUTE_UNUSED bool succeeded = removeStruct(tyToDel.name());
	assert(succeeded);
}

boost::filesystem::path GraphModule::sourceFilePath() const {
	return context().workspacePath() / "src" / (fullName() + ".chimod");
}

Result GraphModule::createNodeTypeFromCCode(boost::string_view         code,
                                            boost::string_view         functionName,
                                            std::vector<std::string>   clangArgs,
                                            std::unique_ptr<NodeType>* toFill) {
	assert(toFill != nullptr);

	Result res;

	// add -I for the .c dir
	clangArgs.push_back("-I");
	clangArgs.push_back(pathToCSources().string());

	std::unique_ptr<llvm::Module> mod;
	// find clang
	auto clangExe = findClang();
	if (clangExe.empty()) {
		res.addEntry("EUKN", "Failed to find clang in path", nlohmann::json::object());
		return res;
	}

	res += compileCToLLVM(clangExe, context().llvmContext(), clangArgs, code, &mod);

	if (!res) { return res; }

	auto llFunc = mod->getFunction(functionName.to_string());

	if (llFunc == nullptr) {
		res.addEntry("EUKN", "Failed to find function in C code",
		             {{"Function Name", functionName.to_string()}, {"C Code", code.to_string()}});
		return res;
	}

	std::vector<NamedDataType> dInputs;
	for (const auto& argument : llFunc->args()) {
		DataType ty;
		context().typeFromModule("lang", stringifyLLVMType(argument.getType()), &ty);
		dInputs.emplace_back(argument.getName(), ty);
	}

	DataType output;
	auto     ret = llFunc->getReturnType();

	if (!ret->isVoidTy()) { context().typeFromModule("lang", stringifyLLVMType(ret), &output); }

	*toFill = std::make_unique<CFuncNode>(*this, code.to_string(), functionName.to_string(),
	                                      clangArgs, dInputs, output);

	return res;
}

}  // namespace chi

max_stars_repo_name: undeadinu/chigraph

max_issues_repo_name: undeadinu/chigraph

max_forks_repo_name: undeadinu/chigraph


text:
//----------------------------------------------------------------------------
/** @file GoBoard.cpp
    See GoBoard.h
*/
//----------------------------------------------------------------------------

#include "SgSystem.h"
#include "GoUctBoard.h"

#include <boost/static_assert.hpp>
#include <algorithm>
#include "GoBoardUtil.h"
#include "SgNbIterator.h"
#include "SgStack.h"

using namespace std;

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

namespace {

/** Do a consistency check.
    Check some data structures for consistency after and before each play
    (and at some other places).
    This is an expensive check and therefore has to be enabled at compile
    time.
*/
const bool CONSISTENCY = false;

} // namespace

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

GoUctBoard::GoUctBoard(const GoBoard& bd)
    : m_const(bd.Size())
{
    m_size = -1;
    Init(bd);
}

GoUctBoard::~GoUctBoard()
{
}

void GoUctBoard::CheckConsistency() const
{
    if (! CONSISTENCY)
        return;
    for (SgPoint p = 0; p < SG_MAXPOINT; ++p)
    {
        if (IsBorder(p))
            continue;
        int c = m_color[p];
        SG_ASSERT_EBW(c);
        int n = 0;
        for (SgNb4Iterator it(p); it; ++it)
            if (m_color[*it] == SG_EMPTY)
                ++n;
        SG_ASSERT(n == NumEmptyNeighbors(p));
        n = 0;
        for (SgNb4Iterator it(p); it; ++it)
            if (m_color[*it] == SG_BLACK)
                ++n;
        SG_ASSERT(n == NumNeighbors(p, SG_BLACK));
        n = 0;
        for (SgNb4Iterator it(p); it; ++it)
            if (m_color[*it] == SG_WHITE)
                ++n;
        SG_ASSERT(n == NumNeighbors(p, SG_WHITE));
        if (c == SG_BLACK || c == SG_WHITE)
            CheckConsistencyBlock(p);
        if (c == SG_EMPTY)
            SG_ASSERT(m_block[p] == 0);
    }
}

void GoUctBoard::CheckConsistencyBlock(SgPoint point) const
{
    SG_ASSERT(Occupied(point));
    SgBlackWhite color = GetColor(point);
    GoPointList stones;
    Block::LibertyList liberties;
    SgMarker mark;
    SgStack<SgPoint,SG_MAXPOINT> stack;
    stack.Push(point);
    bool anchorFound = false;
    const Block* block = m_block[point];
    while (! stack.IsEmpty())
    {
        SgPoint p = stack.Pop();
        if (IsBorder(p) || ! mark.NewMark(p))
            continue;
        if (GetColor(p) == color)
        {
            stones.PushBack(p);
            if (p == block->m_anchor)
                anchorFound = true;
            stack.Push(p - SG_NS);
            stack.Push(p - SG_WE);
            stack.Push(p + SG_WE);
            stack.Push(p + SG_NS);
        }
        else if (GetColor(p) == SG_EMPTY)
            liberties.PushBack(p);
    }
    SG_ASSERT(anchorFound);
    SG_ASSERT(color == block->m_color);
    SG_ASSERT(stones.SameElements(block->m_stones));
    SG_ASSERT(liberties.SameElements(block->m_liberties));
    SG_ASSERT(stones.Length() == NumStones(point));
}

void GoUctBoard::AddLibToAdjBlocks(SgPoint p, SgBlackWhite c)
{
    if (NumNeighbors(p, c) == 0)
        return;
    SgReserveMarker reserve(m_marker2);
    m_marker2.Clear();
    Block* b;
    if (m_color[p - SG_NS] == c && (b = m_block[p - SG_NS]) != 0)
    {
        m_marker2.Include(b->m_anchor);
        b->m_liberties.PushBack(p);
    }
    if (m_color[p + SG_NS] == c && (b = m_block[p + SG_NS]) != 0
        && m_marker2.NewMark(b->m_anchor))
        b->m_liberties.PushBack(p);
    if (m_color[p - SG_WE] == c && (b = m_block[p - SG_WE]) != 0
        && m_marker2.NewMark(b->m_anchor))
        b->m_liberties.PushBack(p);
    if (m_color[p + SG_WE] == c && (b = m_block[p + SG_WE]) != 0
        && ! m_marker2.Contains(b->m_anchor))
        b->m_liberties.PushBack(p);
}

void GoUctBoard::AddStoneToBlock(SgPoint p, Block* block)
{
    // Stone already placed
    SG_ASSERT(IsColor(p, block->m_color));
    block->m_stones.PushBack(p);
    if (IsEmpty(p - SG_NS) && ! IsAdjacentTo(p - SG_NS, block))
        block->m_liberties.PushBack(p - SG_NS);
    if (IsEmpty(p - SG_WE) && ! IsAdjacentTo(p - SG_WE, block))
        block->m_liberties.PushBack(p - SG_WE);
    if (IsEmpty(p + SG_WE) && ! IsAdjacentTo(p + SG_WE, block))
        block->m_liberties.PushBack(p + SG_WE);
    if (IsEmpty(p + SG_NS) && ! IsAdjacentTo(p + SG_NS, block))
        block->m_liberties.PushBack(p + SG_NS);
    m_block[p] = block;
}

void GoUctBoard::CreateSingleStoneBlock(SgPoint p, SgBlackWhite c)
{
    // Stone already placed
    SG_ASSERT(IsColor(p, c));
    SG_ASSERT(NumNeighbors(p, c) == 0);
    Block& block = m_blockArray[p];
    block.InitSingleStoneBlock(c, p);
    if (IsEmpty(p - SG_NS))
        block.m_liberties.PushBack(p - SG_NS);
    if (IsEmpty(p - SG_WE))
        block.m_liberties.PushBack(p - SG_WE);
    if (IsEmpty(p + SG_WE))
        block.m_liberties.PushBack(p + SG_WE);
    if (IsEmpty(p + SG_NS))
        block.m_liberties.PushBack(p + SG_NS);
    m_block[p] = █
}

bool GoUctBoard::IsAdjacentTo(SgPoint p,
                              const GoUctBoard::Block* block) const
{
    return   m_block[p - SG_NS] == block
          || m_block[p - SG_WE] == block
          || m_block[p + SG_WE] == block
          || m_block[p + SG_NS] == block;
}

void GoUctBoard::MergeBlocks(SgPoint p, const SgSList<Block*,4>& adjBlocks)
{
    // Stone already placed
    SG_ASSERT(IsColor(p, adjBlocks[0]->m_color));
    SG_ASSERT(NumNeighbors(p, adjBlocks[0]->m_color) > 1);
    Block* largestBlock = 0;
    int largestBlockStones = 0;
    for (SgSList<Block*,4>::Iterator it(adjBlocks); it; ++it)
    {
        Block* adjBlock = *it;
        int numStones = adjBlock->m_stones.Length();
        if (numStones > largestBlockStones)
        {
            largestBlockStones = numStones;
            largestBlock = adjBlock;
        }
    }
    largestBlock->m_stones.PushBack(p);
    SgReserveMarker reserve(m_marker);
    m_marker.Clear();
    for (Block::LibertyIterator lib(largestBlock->m_liberties); lib; ++lib)
        m_marker.Include(*lib);
    for (SgSList<Block*,4>::Iterator it(adjBlocks); it; ++it)
    {
        Block* adjBlock = *it;
        if (adjBlock == largestBlock)
            continue;
        for (Block::StoneIterator stn(adjBlock->m_stones); stn; ++stn)
        {
            largestBlock->m_stones.PushBack(*stn);
            m_block[*stn] = largestBlock;
        }
        for (Block::LibertyIterator lib(adjBlock->m_liberties); lib; ++lib)
            if (m_marker.NewMark(*lib))
                largestBlock->m_liberties.PushBack(*lib);
    }
    m_block[p] = largestBlock;
    if (IsEmpty(p - SG_NS) && m_marker.NewMark(p - SG_NS))
        largestBlock->m_liberties.PushBack(p - SG_NS);
    if (IsEmpty(p - SG_WE) && m_marker.NewMark(p - SG_WE))
        largestBlock->m_liberties.PushBack(p - SG_WE);
    if (IsEmpty(p + SG_WE) && m_marker.NewMark(p + SG_WE))
        largestBlock->m_liberties.PushBack(p + SG_WE);
    if (IsEmpty(p + SG_NS) && m_marker.NewMark(p + SG_NS))
        largestBlock->m_liberties.PushBack(p + SG_NS);
}

void GoUctBoard::UpdateBlocksAfterAddStone(SgPoint p, SgBlackWhite c,
                                           const SgSList<Block*,4>& adjBlocks)
{
    // Stone already placed
    SG_ASSERT(IsColor(p, c));
    int n = adjBlocks.Length();
    if (n == 0)
        CreateSingleStoneBlock(p, c);
    else
    {
        if (n == 1)
            AddStoneToBlock(p, adjBlocks[0]);
        else
            MergeBlocks(p, adjBlocks);
    }
}

void GoUctBoard::Init(const GoBoard& bd)
{
    if (bd.Size() != m_size)
        InitSize(bd);
    m_prisoners[SG_BLACK] = bd.NumPrisoners(SG_BLACK);
    m_prisoners[SG_WHITE] = bd.NumPrisoners(SG_WHITE);
    m_koPoint = bd.KoPoint();
    m_lastMove = bd.GetLastMove();
    m_secondLastMove = bd.Get2ndLastMove();
    m_toPlay = bd.ToPlay();
    for (GoBoard::Iterator it(bd); it; ++it)
    {
        SgPoint p = *it;
        SgBoardColor c = bd.GetColor(p);
        m_color[p] = c;
        m_nuNeighbors[SG_BLACK][p] = bd.NumNeighbors(p, SG_BLACK);
        m_nuNeighbors[SG_WHITE][p] = bd.NumNeighbors(p, SG_WHITE);
        m_nuNeighborsEmpty[p] = bd.NumEmptyNeighbors(p);
        if (bd.IsEmpty(p))
            m_block[p] = 0;
        else if (bd.Anchor(p) == p)
        {
            SgBoardColor c = m_color[p];
            Block& block = m_blockArray[p];
            block.InitNewBlock(c, p);
            for (GoBoard::StoneIterator it2(bd, p); it2; ++it2)
            {
                block.m_stones.PushBack(*it2);
                m_block[*it2] = █
            }
            for (GoBoard::LibertyIterator it2(bd, p); it2; ++it2)
                block.m_liberties.PushBack(*it2);
        }
    }
    CheckConsistency();
}

void GoUctBoard::InitSize(const GoBoard& bd)
{
    m_size = bd.Size();
    m_nuNeighbors[SG_BLACK].Fill(0);
    m_nuNeighbors[SG_WHITE].Fill(0);
    m_nuNeighborsEmpty.Fill(0);
    m_block.Fill(0);
    for (SgPoint p = 0; p < SG_MAXPOINT; ++p)
    {
        if (bd.IsBorder(p))
        {
            m_color[p] = SG_BORDER;
            m_isBorder[p] = true;
        }
        else
            m_isBorder[p] = false;
    }
    m_const.ChangeSize(m_size);
}

void GoUctBoard::NeighborBlocks(SgPoint p, SgBlackWhite c,
                                SgPoint anchors[]) const
{
    SG_ASSERT(IsEmpty(p));
    SgReserveMarker reserve(m_marker);
    SG_UNUSED(reserve);
    m_marker.Clear();
    int i = 0;
    if (NumNeighbors(p, c) > 0)
    {
        if (IsColor(p - SG_NS, c) && m_marker.NewMark(Anchor(p - SG_NS)))
            anchors[i++] = Anchor(p - SG_NS);
        if (IsColor(p - SG_WE, c) && m_marker.NewMark(Anchor(p - SG_WE)))
            anchors[i++] = Anchor(p - SG_WE);
        if (IsColor(p + SG_WE, c) && m_marker.NewMark(Anchor(p + SG_WE)))
            anchors[i++] = Anchor(p + SG_WE);
        if (IsColor(p + SG_NS, c) && m_marker.NewMark(Anchor(p + SG_NS)))
            anchors[i++] = Anchor(p + SG_NS);
    }
    anchors[i] = SG_ENDPOINT;
}

void GoUctBoard::AddStone(SgPoint p, SgBlackWhite c)
{
    SG_ASSERT(IsEmpty(p));
    SG_ASSERT_BW(c);
    m_color[p] = c;
    --m_nuNeighborsEmpty[p - SG_NS];
    --m_nuNeighborsEmpty[p - SG_WE];
    --m_nuNeighborsEmpty[p + SG_WE];
    --m_nuNeighborsEmpty[p + SG_NS];
    SgArray<int,SG_MAXPOINT>& nuNeighbors = m_nuNeighbors[c];
    ++nuNeighbors[p - SG_NS];
    ++nuNeighbors[p - SG_WE];
    ++nuNeighbors[p + SG_WE];
    ++nuNeighbors[p + SG_NS];
}

/** Remove liberty from adjacent blocks and kill opponent blocks without
    liberties.
    As a side effect, computes adjacent blocks of own color to avoid a
    second call to GetAdjacentBlocks() in UpdateBlocksAfterAddStone().
*/
void GoUctBoard::RemoveLibAndKill(SgPoint p, SgBlackWhite opp,
                                  SgSList<Block*,4>& ownAdjBlocks)
{
    SgReserveMarker reserve(m_marker);
    m_marker.Clear();
    Block* b;
    if ((b = m_block[p - SG_NS]) != 0)
    {
        m_marker.Include(b->m_anchor);
        b->m_liberties.Exclude(p);
        if (b->m_color == opp)
        {
            if (b->m_liberties.Length() == 0)
                KillBlock(b);
        }
        else
            ownAdjBlocks.PushBack(b);
    }
    if ((b = m_block[p - SG_WE]) != 0 && m_marker.NewMark(b->m_anchor))
    {
        b->m_liberties.Exclude(p);
        if (b->m_color == opp)
        {
            if (b->m_liberties.Length() == 0)
                KillBlock(b);
        }
        else
            ownAdjBlocks.PushBack(b);
    }
    if ((b = m_block[p + SG_WE]) != 0 && m_marker.NewMark(b->m_anchor))
    {
        b->m_liberties.Exclude(p);
        if (b->m_color == opp)
        {
            if (b->m_liberties.Length() == 0)
                KillBlock(b);
        }
        else
            ownAdjBlocks.PushBack(b);
    }
    if ((b = m_block[p + SG_NS]) != 0 && ! m_marker.Contains(b->m_anchor))
    {
        b->m_liberties.Exclude(p);
        if (b->m_color == opp)
        {
            if (b->m_liberties.Length() == 0)
                KillBlock(b);
        }
        else
            ownAdjBlocks.PushBack(b);
    }
}

void GoUctBoard::KillBlock(const Block* block)
{
    SgBlackWhite c = block->m_color;
    SgBlackWhite opp = SgOppBW(c);
    SgArray<int,SG_MAXPOINT>& nuNeighbors = m_nuNeighbors[c];
    for (Block::StoneIterator it(block->m_stones); it; ++it)
    {
        SgPoint p = *it;
        AddLibToAdjBlocks(p, opp);
        m_color[p] = SG_EMPTY;
        ++m_nuNeighborsEmpty[p - SG_NS];
        ++m_nuNeighborsEmpty[p - SG_WE];
        ++m_nuNeighborsEmpty[p + SG_WE];
        ++m_nuNeighborsEmpty[p + SG_NS];
        --nuNeighbors[p - SG_NS];
        --nuNeighbors[p - SG_WE];
        --nuNeighbors[p + SG_WE];
        --nuNeighbors[p + SG_NS];
        m_capturedStones.PushBack(p);
        m_block[p] = 0;
    }
    int nuStones = block->m_stones.Length();
    m_prisoners[c] += nuStones;
    if (nuStones == 1)
        // Remember that single stone was captured, check conditions on
        // capturing block later
        m_koPoint = block->m_anchor;
}

void GoUctBoard::Play(SgPoint p)
{
    SG_ASSERT(p >= 0); // No special move, see SgMove
    SG_ASSERT(p == SG_PASS || (IsValidPoint(p) && IsEmpty(p)));
    CheckConsistency();
    m_koPoint = SG_NULLPOINT;
    m_capturedStones.Clear();
    SgBlackWhite opp = SgOppBW(m_toPlay);
    if (p != SG_PASS)
    {
        AddStone(p, m_toPlay);
        SgSList<Block*,4> adjBlocks;
        if (NumNeighbors(p, SG_BLACK) > 0 || NumNeighbors(p, SG_WHITE) > 0)
            RemoveLibAndKill(p, opp, adjBlocks);
        UpdateBlocksAfterAddStone(p, m_toPlay, adjBlocks);
        if (m_koPoint != SG_NULLPOINT)
            if (NumStones(p) > 1 || NumLiberties(p) > 1)
                m_koPoint = SG_NULLPOINT;
        SG_ASSERT(HasLiberties(p)); // Suicide not supported by GoUctBoard
    }
    m_secondLastMove = m_lastMove;
    m_lastMove = p;
    m_toPlay = opp;
    CheckConsistency();
}

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

max_stars_repo_name: MisterTea/HyperNEAT

max_issues_repo_name: afcarl/HyperNEAT

max_forks_repo_name: afcarl/HyperNEAT


text:
/*************************************************************************
 * Dicomifier - Copyright (C) Universite de Strasbourg
 * Distributed under the terms of the CeCILL-B license, as published by
 * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
 * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
 * for details.
 ************************************************************************/

#define BOOST_TEST_MODULE grammar
#include <boost/test/unit_test.hpp>

#include <string>

#include "bruker/grammar.h"
#include "bruker/Field.h"
#include "core/Exception.h"

std::vector<dicomifier::bruker::Field> parse(std::string const & value)
{
    auto begin = value.begin();
    auto const end = value.end();

    std::vector<dicomifier::bruker::Field> fields;
    dicomifier::bruker::grammar<std::string::const_iterator> g;
    auto const parsed = boost::spirit::qi::parse(begin, end, g, fields);

    // Make sure everything was parsed
    BOOST_REQUIRE(parsed);
    BOOST_REQUIRE(begin == end);

    return fields;
}

BOOST_AUTO_TEST_CASE(Comment)
{
    std::string const value = "$$Comment";
    auto const fields = parse(value);

    BOOST_REQUIRE(fields.empty());
}

BOOST_AUTO_TEST_CASE(UnquotedString)
{
    std::string const value = "##FieldName=A few words";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value({"A few words"}));
}

BOOST_AUTO_TEST_CASE(QuotedStrings)
{
    std::string const value = "##FieldName=( 3, 5 )\n<foo> <bar>";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({3, 5}));
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value({"foo", "bar"}));
}

BOOST_AUTO_TEST_CASE(Real)
{
    std::string const value = "##FieldName=1.23";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE_EQUAL(fields[0].value.size(), 1);
    BOOST_REQUIRE(
        abs((boost::get<double const>(fields[0].value[0]) - 1.23f)/1.23f) < 1e-6);
}

BOOST_AUTO_TEST_CASE(NotReal)
{
    std::string const value = "##FieldName=1.23 foo";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value{"1.23 foo"});
}

BOOST_AUTO_TEST_CASE(Reals)
{
    std::string const value = "##FieldName=( 2 )\n1.23 -4.56";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({2}));
    BOOST_REQUIRE_EQUAL(fields[0].value.size(), 2);
    BOOST_REQUIRE(
        abs((boost::get<double const>(fields[0].value[0]) - 1.23f)/1.23f) < 1e-6);
    BOOST_REQUIRE(
        abs((boost::get<double const>(fields[0].value[1]) - -4.56f)/4.56f) < 1e-6);
}

BOOST_AUTO_TEST_CASE(Integer)
{
    std::string const value = "##FieldName=123";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value{123L});
}

BOOST_AUTO_TEST_CASE(NotInteger)
{
    std::string const value = "##FieldName=123 foo";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value{"123 foo"});
}

BOOST_AUTO_TEST_CASE(Integers)
{
    std::string const value = "##FieldName=( 2 )\n123 -456";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({2}));
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value({123L, -456L}));
}

BOOST_AUTO_TEST_CASE(Structure)
{
    std::string const value = "##FieldName=( 1 )\n(3, <Foo>)";
    auto const fields = parse(value);

    BOOST_REQUIRE_EQUAL(fields.size(), 1);
    BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName");
    BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({1}));

    dicomifier::bruker::Field::Value const item({3L, "Foo"});
    BOOST_REQUIRE(
        fields[0].value == dicomifier::bruker::Field::Value({{item}}));
}

max_stars_repo_name: DimitriPapadopoulos/dicomifier

max_issues_repo_name: DimitriPapadopoulos/dicomifier

max_forks_repo_name: DimitriPapadopoulos/dicomifier


text:
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <future>

#include "EmptyPort.hpp"
#include "TestServer.hpp"
#include "catch.hpp"

#include "bredis/Connection.hpp"
#include "bredis/Extract.hpp"

#include "SocketWithLogging.hpp"

namespace r = bredis;
namespace asio = boost::asio;
namespace ep = empty_port;
namespace ts = test_server;

TEST_CASE("ping", "[connection]") {
    using socket_t = asio::ip::tcp::socket;
#ifdef BREDIS_DEBUG
    using next_layer_t = r::test::SocketWithLogging<socket_t>;
#else
    using next_layer_t = socket_t;
#endif
    using Buffer = boost::asio::streambuf;
    using Iterator =
        boost::asio::buffers_iterator<typename Buffer::const_buffers_type,
                                      char>;
    using Policy = r::parsing_policy::keep_result;
    using ParseResult = r::positive_parse_result_t<Policy>;
    using Extractor = r::extractor;

    using result_t = void;
    using read_callback_t = std::function<void(
        const boost::system::error_code &error_code, ParseResult &&r)>;

    std::chrono::milliseconds sleep_delay(1);

    uint16_t port = ep::get_random<ep::Kind::TCP>();
    auto port_str = boost::lexical_cast<std::string>(port);
    auto server = ts::make_server({"redis-server", "--port", port_str});
    ep::wait_port<ep::Kind::TCP>(port);
    asio::io_service io_service;

    asio::ip::tcp::endpoint end_point(
        asio::ip::address::from_string("127.0.0.1"), port);
    socket_t socket(io_service, end_point.protocol());
    socket.connect(end_point);

    r::Connection<next_layer_t> c(std::move(socket));
    std::promise<result_t> completion_promise;
    std::future<result_t> completion_future = completion_promise.get_future();

    int order = 0;
    Buffer rx_buff, tx_buff;

    r::command_container_t cmds_container{
        r::single_command_t("LLEN", "x"),
        r::single_command_t("GET", "x"),
        r::single_command_t("SET", "x", "value"),
        r::single_command_t("GET", "x"),
        r::single_command_t("LLEN"),
        r::single_command_t("time"),
    };
    std::vector<read_callback_t> callbacks{
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            auto extract = std::visit(Extractor(), r.result);
            REQUIRE(std::get<r::extracts::int_t>(extract) == 0);
            REQUIRE(order == 0);
        },
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            auto extract = std::visit(Extractor(), r.result);
            REQUIRE(std::get_if<r::extracts::nil_t>(&extract));
            REQUIRE(order == 1);
        },
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            auto extract = std::visit(Extractor(), r.result);
            REQUIRE(std::get<r::extracts::string_t>(extract).str == "OK");
            REQUIRE(order == 2);
        },
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            auto extract = std::visit(Extractor(), r.result);
            REQUIRE(std::get<r::extracts::string_t>(extract).str == "value");
            REQUIRE(order == 3);
        },
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            auto extract = std::visit(Extractor(), r.result);
            REQUIRE(std::get<r::extracts::error_t>(extract).str ==
                    "ERR wrong number of arguments for 'llen' command");
            REQUIRE(order == 4);
        },
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            REQUIRE(order == 5);
            auto extract = std::visit(Extractor(), r.result);
            auto &arr = std::get<r::extracts::array_holder_t>(extract);
            REQUIRE(arr.elements.size() == 2);
            auto &c1 = std::get<r::extracts::string_t>(arr.elements[0]).str;
            auto &c2 = std::get<r::extracts::string_t>(arr.elements[1]).str;

            REQUIRE(boost::lexical_cast<r::extracts::int_t>(c1) >= 0);
            REQUIRE(boost::lexical_cast<r::extracts::int_t>(c2) >= 0);
            completion_promise.set_value();
        }};

    read_callback_t generic_callback =
        [&](const boost::system::error_code &error_code, ParseResult &&r) {
            REQUIRE(!error_code);
            auto &cb = callbacks[order];
            auto consumed = r.consumed;
            cb(error_code, std::move(r));
            rx_buff.consume(consumed);
            ++order;
            c.async_read(rx_buff, generic_callback);
        };

    c.async_write(tx_buff, r::command_wrapper_t(cmds_container),
                  [&](const auto &error_code, auto bytes_transferred) {
                      REQUIRE(!error_code);
                      tx_buff.consume(bytes_transferred);
                      c.async_read(rx_buff, generic_callback);
                  });

    while (completion_future.wait_for(sleep_delay) !=
           std::future_status::ready) {
        io_service.run_one();
    }
};

max_stars_repo_name: HungMingWu/cpp-bredis

max_issues_repo_name: HungMingWu/cpp-bredis

max_forks_repo_name: HungMingWu/cpp-bredis


text:
/**
 * \file endpoint.cpp
 * \author Julien Kauffmann <julien.kauffmann@freelan.org>
 * \brief A endpoint test sample.
 */

#include <asiotap/types/endpoint.hpp>
#include <asiotap/types/ip_network_address.hpp>
#include <asiotap/types/ip_route.hpp>

#include <cstdlib>
#include <csignal>
#include <iostream>

#include <boost/lexical_cast.hpp>

int main()
{
	try
	{
		asiotap::endpoint ep1 = asiotap::hostname_endpoint("localhost", "1234");
		asiotap::endpoint ep2 = asiotap::ipv4_endpoint(boost::asio::ip::address_v4::loopback(), 1234);
		asiotap::endpoint ep3 = asiotap::ipv6_endpoint(boost::asio::ip::address_v6::loopback(), 1234);
		asiotap::ipv4_network_address na1 = asiotap::ipv4_network_address(boost::asio::ip::address_v4::loopback(), 24);
		asiotap::ipv6_network_address na2 = asiotap::ipv6_network_address(boost::asio::ip::address_v6::loopback(), 64);
		asiotap::ipv4_network_address na3 = asiotap::ipv4_network_address(boost::asio::ip::address_v4::loopback());
		asiotap::ipv4_route r1 = asiotap::ipv4_route(na1);
		asiotap::ipv6_route r2 = asiotap::ipv6_route(na2);
		asiotap::ipv4_route r3 = asiotap::ipv4_route(na3);

		// Output
		std::cout << "ep1: " << ep1 << std::endl;
		std::cout << "ep2: " << ep2 << std::endl;
		std::cout << "ep3: " << ep3 << std::endl;
		std::cout << "na1: " << na1 << std::endl;
		std::cout << "na2: " << na2 << std::endl;
		std::cout << "na3: " << na3 << std::endl;
		std::cout << "r1: " << r1 << std::endl;
		std::cout << "r2: " << r2 << std::endl;
		std::cout << "r3: " << r3 << std::endl;

		// Comparison
		std::cout << "ep1 == ep2: " << (ep1 == ep2) << std::endl;
		std::cout << "ep1 != ep2: " << (ep1 != ep2) << std::endl;

		// Conversions
		std::cout << boost::lexical_cast<asiotap::endpoint>("127.0.0.1:45") << std::endl;
		std::cout << boost::lexical_cast<asiotap::endpoint>("[3::4:5ae]:78") << std::endl;
		std::cout << boost::lexical_cast<asiotap::endpoint>("some.host.com:service") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv4_network_address>("9.0.0.1/24") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv6_network_address>("fe80::1/10") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_network_address>("9.0.0.1/24") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_network_address>("fe80::1/10") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_network_address>("127.0.0.1") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_network_address>("fe80::1") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv4_route>("9.0.0.1/24") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv4_route>("9.0.0.1/24 => 9.0.0.255") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv6_route>("fe80::1/10") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ipv6_route>("fe80::1/10 => fe80::ff") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("9.0.0.1/24") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1/10") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("127.0.0.1") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("127.0.0.1 => 127.0.0.255") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1 => fe80::ff") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1/10=>fe80::ff") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1/10 =>fe80::ff") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1/10=> fe80::ff") << std::endl;
		std::cout << boost::lexical_cast<asiotap::ip_route>("fe80::1/10\t=>\tfe80::ff") << std::endl;

		// Create a route from its components.
		const auto ina = boost::lexical_cast<asiotap::ip_network_address>("192.168.0.0/24");
		const boost::optional<boost::asio::ip::address> gw = get_network_address(boost::lexical_cast<asiotap::ip_network_address>("192.168.0.254"));
		const auto route = to_ip_route(ina, gw);
		std::cout << route << std::endl;
	}
	catch (std::exception& ex)
	{
		std::cerr << "Exception caught: " << ex.what() << std::endl;

		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

max_stars_repo_name: krattai/AEBL

max_issues_repo_name: krattai/AEBL

max_forks_repo_name: krattai/AEBL


text: #include <boost/archive/polymorphic_xml_woarchive.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include <iostream>

#include <cstring>

#include <Magick++.h>
#include <Magick++/Blob.h>

#include <boost/numeric/ublas/matrix.hpp>

int main(void)
{

  const int h = 256;
  const int w = 256;

  boost::numeric::ublas::matrix< unsigned char > band(h, w);
  for (int y=0; y<h; ++y)
    for(int x=0; x<w; ++x)
      {
	//band(y,x) = y;
	band(y,x) = static_cast<unsigned char>( (y*x) % 256 );
      }

  const unsigned char* __restrict bandPtr = (&(band.data()[0]));

  const int bandLen = sizeof(unsigned char) * h * w;

  char* buffer = new char [ bandLen * 3 ];
  for(int i=0; i<bandLen; ++i)
    {
      buffer[i*3 + 0] = buffer[i*3 + 1] = buffer[i*3 + 2] = bandPtr[i];
    }

  Magick::Blob inputBlob(buffer, 3*bandLen);

  Magick::Image image(inputBlob, Magick::Geometry(w,h), 8, "RGB");

  Magick::Blob outputBlob;
  image.write(&outputBlob, "JPEG");

  FILE* fp = fopen("out.jpg", "w");
  fwrite(outputBlob.data(), 1, outputBlob.length(), fp);
  fclose(fp);

  return 0;
}

max_stars_repo_name: klaricmn/snippets

max_issues_repo_name: klaricmn/snippets

max_forks_repo_name: klaricmn/snippets


text:
#include <cmath>
#include <dlfcn.h>

#include <sys/stat.h>

#include <ros/ros.h>
#include "sensor_msgs/CompressedImage.h"

#include <boost/bind.hpp>

#include "basetime/basetime.h"

typedef bt_err (*bt_get_relative_time_t)(unsigned int *sec, unsigned int *nsec);

const double rad2deg = 180 / M_PI;

unsigned char seq = 0;

void encode(const std::vector<uint8_t> & data, FILE *fp, bt_get_relative_time_t bt_get_relative_time){
  unsigned char type = 1;
  if (fwrite(&type, sizeof(unsigned char), 1, fp) != 1 ){
    ROS_ERROR("failed to write fifo: type");
    std::exit(1);
  }

  unsigned int vlen = data.size() + 10;
  unsigned char vlen_byte[3] = {static_cast<unsigned char>(vlen & 0x0000FF),
  static_cast<unsigned char>((vlen & 0x00FF00) >> 8),
  static_cast<unsigned char>((vlen & 0xFF0000) >> 16)};
  if (fwrite(vlen_byte, sizeof(unsigned char), 3, fp) != 3){
    ROS_ERROR("failed to write fifo: vlen");
    std::exit(1);
  }

  unsigned int sec, nsec;
  bt_get_relative_time(&sec, &nsec);
    if (fwrite(&sec, sizeof(unsigned int), 1, fp) != 1){
    ROS_ERROR("failed to write fifo: sec");
    std::exit(1);
  }
  if (fwrite(&nsec, sizeof(unsigned int), 1, fp) != 1){
    ROS_ERROR("failed to write fifo: nsec");
    std::exit(1);
  }

  // must use mutex to get seq
  unsigned char dtype = 0x12;
  if (fwrite(&dtype, sizeof(unsigned char), 1, fp) != 1){
    ROS_ERROR("failed to write fifo: dtype");
    std::exit(1);
  }
  if (fwrite(&seq, sizeof(unsigned char), 1, fp) != 1){
    ROS_ERROR("failed to write fifo: seq");
    std::exit(1);
  }
  seq = (seq + 1) % 256;

  if (fwrite(&data[0], sizeof(uint8_t), data.size(), fp) != data.size()){
    ROS_ERROR("failed to write fifo: name");
    std::exit(1);
  }
}

void CallbackImage(const sensor_msgs::CompressedImage::ConstPtr & msg, FILE*fp, bt_get_relative_time_t bt_get_relative_time){
    encode(msg->data, fp, bt_get_relative_time);
}

bool existFile(const char* path)
{
    struct stat st;

    if (stat(path, &st) != 0) {
        return false;
    }
    return true;
}

void WaitPathCreation(const std::string &path){
  ros::Rate wait(1);
  while(ros::ok()){
    bool tmp = existFile(path.c_str());
    if (tmp)break;
    wait.sleep();
  }
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "jpeghook");
  ros::NodeHandle nh;

  std::string fifo_path;
  if (!ros::param::get("~fifo_path", fifo_path)){
    ROS_ERROR("fifo_path doesn't exist");
    std::exit(1);
  }
  WaitPathCreation(fifo_path);
  FILE *fp = fopen(fifo_path.c_str(), "wb");
  if (fp == NULL){
    ROS_ERROR("fifo path doen't exist");
    std::exit(1);
  }

  std::string basetime_path;
  if (!ros::param::get("~basetime_path", basetime_path)){
    ROS_ERROR("basetime_path doesn't exist");
    std::exit(1);
  }
  WaitPathCreation(basetime_path);
  std::string basetime_lib_path;
  if (!ros::param::get("~basetime_lib_path", basetime_lib_path)){
    ROS_ERROR("basetime_lib_path doesn't exist");
    std::exit(1);
  }

  void *handle;
  handle = dlopen(basetime_lib_path.c_str(), RTLD_NOW);
  typedef bt_err (*bt_init_t)(const char* btfile, int create_base);
  bt_init_t bt_init;
  bt_init = reinterpret_cast<bt_init_t>(reinterpret_cast<long >(dlsym(handle, "bt_init")));
  char *error = dlerror();
  if (error != NULL){
    ROS_ERROR("failed to load bt_init");
    std::exit(1);
  }
  bt_init(basetime_path.c_str(), false);

  bt_get_relative_time_t bt_get_relative_time;
  bt_get_relative_time = reinterpret_cast<bt_get_relative_time_t>(reinterpret_cast<long >(dlsym(handle, "bt_get_relative_time")));
  error = dlerror();
  if (error != NULL){
    ROS_ERROR("failed to load bt_get_relative_time");
    std::exit(1);
  }

  std::string topic_name;
  if (!ros::param::get("~topic", topic_name)){
    ROS_ERROR("topic doesn't exist");
    std::exit(1);
  }

  ros::Subscriber sub = nh.subscribe<sensor_msgs::CompressedImage>(topic_name, 100, boost::bind(CallbackImage, _1, fp, bt_get_relative_time));

  ros::spin();
  fclose(fp);
  return 0;
}


text:
/*
 * Copyright (c) 2016, TNO IVS Helmond.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "robot_localization/ros_robot_localization_listener.h"
#include "robot_localization/ros_filter_utilities.h"

#include <map>
#include <string>
#include <vector>

#include <Eigen/Dense>
#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <yaml-cpp/yaml.h>
#include <eigen_conversions/eigen_msg.h>

#include <XmlRpcException.h>

namespace RobotLocalization
{

FilterType filterTypeFromString(const std::string& filter_type_str)
{
  if ( filter_type_str == "ekf" )
  {
    return FilterTypes::EKF;
  }
  else if ( filter_type_str == "ukf" )
  {
    return FilterTypes::UKF;
  }
  else
  {
    return FilterTypes::NotDefined;
  }
}

RosRobotLocalizationListener::RosRobotLocalizationListener():
  nh_p_("robot_localization"),
  odom_sub_(nh_, "odometry/filtered", 1),
  accel_sub_(nh_, "accel/filtered", 1),
  sync_(odom_sub_, accel_sub_, 10),
  base_frame_id_(""),
  world_frame_id_(""),
  tf_listener_(tf_buffer_)
{
  int buffer_size;
  nh_p_.param("buffer_size", buffer_size, 10);

  std::string param_ns;
  nh_p_.param("parameter_namespace", param_ns, nh_p_.getNamespace());

  ros::NodeHandle nh_param(param_ns);

  std::string filter_type_str;
  nh_param.param("filter_type", filter_type_str, std::string("ekf"));
  FilterType filter_type = filterTypeFromString(filter_type_str);
  if ( filter_type == FilterTypes::NotDefined )
  {
    ROS_ERROR("RosRobotLocalizationListener: Parameter filter_type invalid");
    return;
  }

  // Load up the process noise covariance (from the launch file/parameter server)
  // TODO(reinzor): this code is copied from ros_filter. In a refactor, this could be moved to a function in
  // ros_filter_utilities
  Eigen::MatrixXd process_noise_covariance(STATE_SIZE, STATE_SIZE);
  process_noise_covariance.setZero();
  XmlRpc::XmlRpcValue process_noise_covar_config;

  if (!nh_param.hasParam("process_noise_covariance"))
  {
    ROS_FATAL_STREAM("Process noise covariance not found in the robot localization listener config (namespace " <<
                     nh_param.getNamespace() << ")! Use the ~parameter_namespace to specify the parameter namespace.");
  }
  else
  {
    try
    {
      nh_param.getParam("process_noise_covariance", process_noise_covar_config);

      ROS_ASSERT(process_noise_covar_config.getType() == XmlRpc::XmlRpcValue::TypeArray);

      int mat_size = process_noise_covariance.rows();

      for (int i = 0; i < mat_size; i++)
      {
        for (int j = 0; j < mat_size; j++)
        {
          try
          {
            // These matrices can cause problems if all the types
            // aren't specified with decimal points. Handle that
            // using string streams.
            std::ostringstream ostr;
            process_noise_covar_config[mat_size * i + j].write(ostr);
            std::istringstream istr(ostr.str());
            istr >> process_noise_covariance(i, j);
          }
          catch(XmlRpc::XmlRpcException &e)
          {
            throw e;
          }
          catch(...)
          {
            throw;
          }
        }
      }

      ROS_DEBUG_STREAM("Process noise covariance is:\n" << process_noise_covariance << "\n");
    }
    catch (XmlRpc::XmlRpcException &e)
    {
      ROS_ERROR_STREAM("ERROR reading robot localization listener config: " <<
                       e.getMessage() <<
                       " for process_noise_covariance (type: " <<
                       process_noise_covar_config.getType() << ")");
    }
  }

  std::vector<double> filter_args;
  nh_param.param("filter_args", filter_args, std::vector<double>());

  estimator_ = new RobotLocalizationEstimator(buffer_size, filter_type, process_noise_covariance, filter_args);

  sync_.registerCallback(&RosRobotLocalizationListener::odomAndAccelCallback, this);

  ROS_INFO_STREAM("Ros Robot Localization Listener: Listening to topics " <<
                  odom_sub_.getTopic() << " and " << accel_sub_.getTopic());

  // Wait until the base and world frames are set by the incoming messages
  while (ros::ok() && base_frame_id_.empty())
  {
    ros::spinOnce();
    ROS_INFO_STREAM_THROTTLE(1.0, "Ros Robot Localization Listener: Waiting for incoming messages on topics " <<
                             odom_sub_.getTopic() << " and " << accel_sub_.getTopic());
    ros::Duration(0.1).sleep();
  }
}

RosRobotLocalizationListener::~RosRobotLocalizationListener()
{
  delete estimator_;
}

void RosRobotLocalizationListener::odomAndAccelCallback(const nav_msgs::Odometry& odom,
                                                        const geometry_msgs::AccelWithCovarianceStamped& accel)
{
  // Instantiate a state that can be added to the robot localization estimator
  EstimatorState state;

  // Set its time stamp and the state received from the messages
  state.time_stamp = odom.header.stamp.toSec();

  // Get the base frame id from the odom message
  if ( base_frame_id_.empty() )
    base_frame_id_ = odom.child_frame_id;

  // Get the world frame id from the odom message
  if ( world_frame_id_.empty() )
    world_frame_id_ = odom.header.frame_id;

  // Pose: Position
  state.state(StateMemberX) = odom.pose.pose.position.x;
  state.state(StateMemberY) = odom.pose.pose.position.y;
  state.state(StateMemberZ) = odom.pose.pose.position.z;

  // Pose: Orientation
  tf2::Quaternion orientation_quat;
  tf2::fromMsg(odom.pose.pose.orientation, orientation_quat);
  double roll, pitch, yaw;
  RosFilterUtilities::quatToRPY(orientation_quat, roll, pitch, yaw);

  state.state(StateMemberRoll) = roll;
  state.state(StateMemberPitch) = pitch;
  state.state(StateMemberYaw) = yaw;

  // Pose: Covariance
  for ( unsigned int i = 0; i < POSE_SIZE; i++ )
  {
    for ( unsigned int j = 0; j < POSE_SIZE; j++ )
    {
      state.covariance(POSITION_OFFSET + i, POSITION_OFFSET + j) = odom.pose.covariance[i*POSE_SIZE + j];
    }
  }

  // Velocity: Linear
  state.state(StateMemberVx) = odom.twist.twist.linear.x;
  state.state(StateMemberVy) = odom.twist.twist.linear.y;
  state.state(StateMemberVz) = odom.twist.twist.linear.z;

  // Velocity: Angular
  state.state(StateMemberVroll) = odom.twist.twist.angular.x;
  state.state(StateMemberVpitch) = odom.twist.twist.angular.y;
  state.state(StateMemberVyaw) = odom.twist.twist.angular.z;

  // Velocity: Covariance
  for ( unsigned int i = 0; i < TWIST_SIZE; i++ )
  {
    for ( unsigned int j = 0; j < TWIST_SIZE; j++ )
    {
      state.covariance(POSITION_V_OFFSET + i, POSITION_V_OFFSET + j) = odom.twist.covariance[i*TWIST_SIZE + j];
    }
  }

  // Acceleration: Linear
  state.state(StateMemberAx) = accel.accel.accel.linear.x;
  state.state(StateMemberAy) = accel.accel.accel.linear.y;
  state.state(StateMemberAz) = accel.accel.accel.linear.z;

  // Acceleration: Angular is not available in state

  // Acceleration: Covariance
  for ( unsigned int i = 0; i < ACCELERATION_SIZE; i++ )
  {
    for ( unsigned int j = 0; j < ACCELERATION_SIZE; j++ )
    {
      state.covariance(POSITION_A_OFFSET + i, POSITION_A_OFFSET + j) = accel.accel.covariance[i*TWIST_SIZE + j];
    }
  }

  // Add the state to the buffer, so that we can later interpolate between this and earlier states
  estimator_->setState(state);

  return;
}

bool findAncestorRecursiveYAML(YAML::Node& tree, const std::string& source_frame, const std::string& target_frame)
{
  if ( source_frame == target_frame )
  {
    return true;
  }

  std::string parent_frame = tree[source_frame]["parent"].Scalar();

  if ( parent_frame.empty() )
  {
    return false;
  }

  return findAncestorRecursiveYAML(tree, parent_frame, target_frame);
}

// Cache, assumption that the tree parent child order does not change over time
static std::map<std::string, std::vector<std::string> > ancestor_map;
static std::map<std::string, std::vector<std::string> > descendant_map;
bool findAncestor(const tf2_ros::Buffer& buffer, const std::string& source_frame, const std::string& target_frame)
{
  // Check cache
  const std::vector<std::string>& ancestors = ancestor_map[source_frame];
  if (std::find(ancestors.begin(), ancestors.end(), target_frame) != ancestors.end())
  {
    return true;
  }
  const std::vector<std::string>& descendants = descendant_map[source_frame];
  if (std::find(descendants.begin(), descendants.end(), target_frame) != descendants.end())
  {
    return false;
  }

  std::stringstream frames_stream(buffer.allFramesAsYAML());
  YAML::Node frames_yaml = YAML::Load(frames_stream);

  bool target_frame_is_ancestor = findAncestorRecursiveYAML(frames_yaml, source_frame, target_frame);
  bool target_frame_is_descendant = findAncestorRecursiveYAML(frames_yaml, target_frame, source_frame);

  // Caching
  if (target_frame_is_ancestor)
  {
    ancestor_map[source_frame].push_back(target_frame);
  }
  if (target_frame_is_descendant)
  {
    descendant_map[source_frame].push_back(target_frame);
  }

  return target_frame_is_ancestor;
}

bool RosRobotLocalizationListener::getState(const double time,
                                            const std::string& frame_id,
                                            Eigen::VectorXd& state,
                                            Eigen::MatrixXd& covariance,
                                            std::string world_frame_id) const
{
  EstimatorState estimator_state;
  state.resize(STATE_SIZE);
  state.setZero();
  covariance.resize(STATE_SIZE, STATE_SIZE);
  covariance.setZero();

  if ( base_frame_id_.empty() || world_frame_id_.empty()  )
  {
    if ( estimator_->getSize() == 0 )
    {
      ROS_WARN("Ros Robot Localization Listener: The base or world frame id is not set. "
               "No odom/accel messages have come in.");
    }
    else
    {
      ROS_ERROR("Ros Robot Localization Listener: The base or world frame id is not set. "
                "Are the child_frame_id and the header.frame_id in the odom messages set?");
    }

    return false;
  }

  if ( estimator_->getState(time, estimator_state) == EstimatorResults::ExtrapolationIntoPast )
  {
    ROS_WARN("Ros Robot Localization Listener: A state is requested at a time stamp older than the oldest in the "
             "estimator buffer. The result is an extrapolation into the past. Maybe you should increase the buffer "
             "size?");
  }

  // If no world_frame_id is specified, we will default to the world frame_id of the received odometry message
  if (world_frame_id.empty())
  {
    world_frame_id = world_frame_id_;
  }

  if ( frame_id == base_frame_id_ && world_frame_id == world_frame_id_ )
  {
    // If the state of the base frame is requested and the world frame equals the world frame of the robot_localization
    // estimator, we can simply return the state we got from the state estimator
    state = estimator_state.state;
    covariance = estimator_state.covariance;
    return true;
  }

  // - - - - - - - - - - - - - - - - - -
  // Get the transformation between the requested world frame and the world_frame of the estimator
  // - - - - - - - - - - - - - - - - - -
  Eigen::Affine3d world_pose_requested_frame;

  // If the requested frame is the same as the tracker, set to identity
  if (world_frame_id == world_frame_id_)
  {
    world_pose_requested_frame.setIdentity();
  }
  else
  {
    geometry_msgs::TransformStamped world_requested_to_world_transform;
    try
    {
      // TODO(reinzor): magic number
      world_requested_to_world_transform = tf_buffer_.lookupTransform(world_frame_id,
                                                                      world_frame_id_,
                                                                      ros::Time(time),
                                                                      ros::Duration(0.1));

      if ( findAncestor(tf_buffer_, world_frame_id, base_frame_id_) )
      {
        ROS_ERROR_STREAM("You are trying to get the state with respect to world frame " << world_frame_id <<
                         ", but this frame is a child of robot base frame " << base_frame_id_ <<
                         ", so this doesn't make sense.");
        return false;
      }
    }
    catch ( const tf2::TransformException &e )
    {
      ROS_WARN_STREAM("Ros Robot Localization Listener: Could not look up transform: " << e.what());
      return false;
    }

    // Convert to pose
    tf::transformMsgToEigen(world_requested_to_world_transform.transform, world_pose_requested_frame);
  }

  // - - - - - - - - - - - - - - - - - -
  // Calculate the state of the requested frame from the state of the base frame.
  // - - - - - - - - - - - - - - - - - -

  // First get the transform from base to target
  geometry_msgs::TransformStamped base_to_target_transform;
  try
  {
    base_to_target_transform = tf_buffer_.lookupTransform(base_frame_id_,
                                                          frame_id,
                                                          ros::Time(time),
                                                          ros::Duration(0.1));  // TODO(reinzor): magic number

    // Check that frame_id is a child of the base frame. If it is not, it does not make sense to request its state.
    // Do this after tf lookup, so we know that there is a connection.
    if ( !findAncestor(tf_buffer_, frame_id, base_frame_id_) )
    {
      ROS_ERROR_STREAM("You are trying to get the state of " << frame_id << ", but this frame is not a child of the "
                                                                            "base frame: " << base_frame_id_ << ".");
      return false;
    }
  }
  catch ( const tf2::TransformException &e )
  {
    ROS_WARN_STREAM("Ros Robot Localization Listener: Could not look up transform: " << e.what());
    return false;
  }

  // And convert it to an eigen Affine transformation
  Eigen::Affine3d target_pose_base;
  tf::transformMsgToEigen(base_to_target_transform.transform, target_pose_base);

  // Then convert the base pose to an Eigen Affine transformation
  Eigen::Vector3d base_position(estimator_state.state(StateMemberX),
                                estimator_state.state(StateMemberY),
                                estimator_state.state(StateMemberZ));

  Eigen::AngleAxisd roll_angle(estimator_state.state(StateMemberRoll), Eigen::Vector3d::UnitX());
  Eigen::AngleAxisd pitch_angle(estimator_state.state(StateMemberPitch), Eigen::Vector3d::UnitY());
  Eigen::AngleAxisd yaw_angle(estimator_state.state(StateMemberYaw), Eigen::Vector3d::UnitZ());

  Eigen::Quaterniond base_orientation(yaw_angle * pitch_angle * roll_angle);

  Eigen::Affine3d base_pose(Eigen::Translation3d(base_position) * base_orientation);

  // Now we can calculate the transform from odom to the requested frame (target)...
  Eigen::Affine3d target_pose_odom = world_pose_requested_frame * base_pose * target_pose_base;

  // ... and put it in the output state
  state(StateMemberX) = target_pose_odom.translation().x();
  state(StateMemberY) = target_pose_odom.translation().y();
  state(StateMemberZ) = target_pose_odom.translation().z();

  Eigen::Vector3d ypr = target_pose_odom.rotation().eulerAngles(2, 1, 0);

  state(StateMemberRoll)  = ypr[2];
  state(StateMemberPitch) = ypr[1];
  state(StateMemberYaw)   = ypr[0];

  // Now let's calculate the twist of the target frame
  // First get the base's twist
  Twist base_velocity;
  Twist target_velocity_base;
  base_velocity.linear = Eigen::Vector3d(estimator_state.state(StateMemberVx),
                                         estimator_state.state(StateMemberVy),
                                         estimator_state.state(StateMemberVz));
  base_velocity.angular = Eigen::Vector3d(estimator_state.state(StateMemberVroll),
                                          estimator_state.state(StateMemberVpitch),
                                          estimator_state.state(StateMemberVyaw));

  // Then calculate the target frame's twist as a result of the base's twist.
  /*
   * We first calculate the coordinates of the velocity vectors (linear and angular) in the base frame. We have to keep
   * in mind that a rotation of the base frame, together with the translational offset of the target frame from the base
   * frame, induces a translational velocity of the target frame.
   */
  target_velocity_base.linear = base_velocity.linear + base_velocity.angular.cross(target_pose_base.translation());
  target_velocity_base.angular = base_velocity.angular;

  // Now we can transform that to the target frame
  Twist target_velocity;
  target_velocity.linear = target_pose_base.rotation().transpose() * target_velocity_base.linear;
  target_velocity.angular = target_pose_base.rotation().transpose() * target_velocity_base.angular;

  state(StateMemberVx) = target_velocity.linear(0);
  state(StateMemberVy) = target_velocity.linear(1);
  state(StateMemberVz) = target_velocity.linear(2);

  state(StateMemberVroll) = target_velocity.angular(0);
  state(StateMemberVpitch) = target_velocity.angular(1);
  state(StateMemberVyaw) = target_velocity.angular(2);

  // Rotate the covariance as well
  Eigen::MatrixXd rot_6d(POSE_SIZE, POSE_SIZE);
  rot_6d.setIdentity();

  rot_6d.block<POSITION_SIZE, POSITION_SIZE>(POSITION_OFFSET, POSITION_OFFSET) = target_pose_base.rotation();
  rot_6d.block<ORIENTATION_SIZE, ORIENTATION_SIZE>(ORIENTATION_OFFSET, ORIENTATION_OFFSET) =
    target_pose_base.rotation();

  // Rotate the covariance
  covariance.block<POSE_SIZE, POSE_SIZE>(POSITION_OFFSET, POSITION_OFFSET) =
      rot_6d * estimator_state.covariance.eval() * rot_6d.transpose();

  return true;
}

bool RosRobotLocalizationListener::getState(const ros::Time& ros_time, const std::string& frame_id,
                                            Eigen::VectorXd& state, Eigen::MatrixXd& covariance,
                                            const std::string& world_frame_id) const
{
  double time;
  if ( ros_time.isZero() )
  {
    ROS_INFO("Ros Robot Localization Listener: State requested at time = zero, returning state at current time");
    time = ros::Time::now().toSec();
  }
  else
  {
    time = ros_time.toSec();
  }

  return getState(time, frame_id, state, covariance, world_frame_id);
}

const std::string& RosRobotLocalizationListener::getBaseFrameId() const
{
  return base_frame_id_;
}

const std::string& RosRobotLocalizationListener::getWorldFrameId() const
{
  return world_frame_id_;
}

}  // namespace RobotLocalization

max_issues_repo_name: acr-iitkgp/Obstacle-Avoidance-on-UAV

max_forks_repo_name: acr-iitkgp/Obstacle-Avoidance-on-UAV