text:
#include <boomhs/billboard.hpp>
#include <boomhs/components.hpp>
#include <boomhs/entity.hpp>
#include <boomhs/level_loader.hpp>
#include <boomhs/material.hpp>
#include <boomhs/obj.hpp>
#include <boomhs/skybox.hpp>
#include <boomhs/tree.hpp>
#include <boomhs/water.hpp>

#include <common/algorithm.hpp>
#include <common/result.hpp>

#include <boost/algorithm/string/predicate.hpp>
#include <extlibs/cpptoml.hpp>

using namespace boomhs;
using namespace opengl;

using CppTableArray = std::shared_ptr<cpptoml::table_array>;
using CppTable      = std::shared_ptr<cpptoml::table>;

#define TRY_OPTION_GENERAL_EVAL(VAR_NAME, V, expr)                                                 \
  auto V{expr};                                                                                \
  if (!V) {                                                                                \
    return cpptoml::option<opengl::AttributePointerInfo>{};                                        \
  }                                                                                \
  VAR_NAME{MOVE(V)};

#define TRY_OPTION_CONCAT(VAR_NAME, TO_CONCAT, expr)                                               \
  TRY_OPTION_GENERAL_EVAL(VAR_NAME, _TRY_OPTION_TEMPORARY_##TO_CONCAT, expr)

#define TRY_OPTION_EXPAND_VAR(VAR_NAME, to_concat, expr)                                           \
  TRY_OPTION_CONCAT(VAR_NAME, to_concat, expr)

// TRY_OPTION
#define TRY_OPTION(VAR_NAME, expr) TRY_OPTION_EXPAND_VAR(VAR_NAME, __COUNTER__, expr)

namespace
{

CppTable
get_table(CppTable const& table, char const* name)
{
  return table->get_table_qualified(name);
}

CppTable
get_table_or_abort(CppTable const& table, char const* name)
{

  auto table_o = get_table(table, name);
  if (!table_o) {
    std::abort();
  }
  return table_o;
}

CppTableArray
get_table_array(CppTable const& table, char const* name)
{
  return table->get_table_array(name);
}

CppTableArray
get_table_array_or_abort(CppTable const& table, char const* name)
{
  auto table_o = get_table_array(table, name);
  if (!table_o) {
    std::abort();
  }
  return table_o;
}

template <typename T>
std::optional<T>
get_value(CppTable const& table, char const* name)
{
  auto cpptoml_option = table->get_as<T>(name);
  if (!cpptoml_option) {
    return std::nullopt;
  }
  // Move value out of cpptoml and into std::optional
  auto value = cpptoml_option.move_out();
  return std::make_optional(MOVE(value));
}

auto
get_string(CppTable const& table, char const* name)
{
  return get_value<std::string>(table, name);
}

auto
get_bool(CppTable const& table, char const* name)
{
  return get_value<bool>(table, name);
}

std::optional<GLsizei>
get_sizei(CppTable const& table, char const* name)
{
  return get_value<GLsizei>(table, name);
}

template <typename T>
auto
get_or_abort(CppTable const& table, char const* name)
{
  auto o = table->get_as<T>(name);
  if (!o) {
    std::abort();
  }
  return *o;
}

std::string
get_string_or_abort(CppTable const& table, char const* name)
{
  return get_or_abort<std::string>(table, name);
}

auto
get_bool_or_abort(CppTable const& table, char const* name)
{
  return get_or_abort<bool>(table, name);
}

std::optional<Color>
get_color(CppTable const& table, char const* name)
{
  auto const load_colors = table->template get_array_of<double>(name);
  if (!load_colors) {
    return std::nullopt;
  }

  std::vector<double> const& c = *load_colors;
  assert(4 == c.size() || 3 == c.size());

  auto const alpha = (3 == c.size()) ? 1.0f : c[3];
  auto color       = color::make_rgba(c[0], c[1], c[2], alpha);
  return std::make_optional(color);
}

Color
get_color_or_abort(CppTable const& table, char const* name)
{
  auto const c = get_color(table, name);
  if (c) {
    return *c;
  }
  std::abort();
}

auto const&
get_first_and_only_entry(CppTable const& table, char const* name)
{
  auto const ta = get_table_array(table, name);

  // Ensure the value retrieved from the table is itself a table, and get a reference to it.
  assert(ta->is_table_array());
  auto const& table_array = ta->get();

  // Confirm there is only one entry in the table array.
  if (1 != table_array.size()) {
    std::abort();
  }

  // Return a reference to the first (and only) entry in the table array.
  return table_array.front();
}

std::optional<glm::vec2>
get_vec2(CppTable const& table, char const* name)
{
  auto const load_data = table->template get_array_of<double>(name);
  if (!load_data) {
    return std::nullopt;
  }
  auto const& ld = *load_data;
  return glm::vec2{ld[0], ld[1]};
}

std::optional<glm::vec3>
get_vec3(CppTable const& table, char const* name)
{
  auto const load_data = table->template get_array_of<double>(name);
  if (!load_data) {
    return std::nullopt;
  }
  auto const& ld = *load_data;
  return glm::vec3{ld[0], ld[1], ld[2]};
}

glm::vec3
get_vec3_or_abort(CppTable const& table, char const* name)
{
  auto const vec3_data = get_vec3(table, name);
  if (!vec3_data) {
    std::abort();
  }
  return *vec3_data;
}

std::optional<int>
get_int(CppTable const& table, char const* name)
{
  auto const load_data = get_value<int>(table, name);
  if (!load_data) {
    return std::nullopt;
  }
  return std::make_optional(*load_data);
}

int
get_int_or_abort(CppTable const& table, char const* name)
{
  auto const data = get_int(table, name);
  if (!data) {
    std::abort();
  }
  return *data;
}

std::optional<unsigned int>
get_unsignedint(CppTable const& table, char const* name)
{
  auto const load_data = get_value<unsigned int>(table, name);
  if (!load_data) {
    return std::nullopt;
  }
  return std::make_optional(*load_data);
}

unsigned int
get_unsignedint_or_abort(CppTable const& table, char const* name)
{
  auto const data = get_unsignedint(table, name);
  if (!data) {
    std::abort();
  }
  return *data;
}

std::optional<float>
get_float(CppTable const& table, char const* name)
{
  auto const load_data = get_value<double>(table, name);
  if (!load_data) {
    return std::nullopt;
  }
  return std::make_optional(static_cast<float>(*load_data));
}

float
get_float_or_abort(CppTable const& table, char const* name)
{
  auto const float_data = get_float(table, name);
  if (!float_data) {
    std::abort();
  }
  return *float_data;
}

Result<ObjStore, LoadStatus>
load_objfiles(common::Logger& logger, CppTableArray const& mesh_table)
{
  auto const load = [&](auto const& table) -> Result<std::pair<std::string, ObjData>, LoadStatus> {
    auto const path = get_string_or_abort(table, "path");
    auto const name = get_string_or_abort(table, "name");
    LOG_TRACE_SPRINTF("Loading objfile name: '%s' path: '%s'", name, path);

    auto const objname = name + ".obj";
    ObjData    objdata = TRY_MOVEOUT(load_objfile(logger, path, objname));
    auto       pair    = std::make_pair(name, MOVE(objdata));
    return OK_MOVE(pair);
  };
  ObjStore store;
  for (auto const& table : *mesh_table) {
    auto pair = TRY_MOVEOUT(load(table));
    store.add_obj(pair.first, MOVE(pair.second));
  }
  return OK_MOVE(store);
}

class ParsedVertexAttributes
{
  using pair_t = std::pair<std::string, opengl::VertexAttribute>;
  std::vector<pair_t> pair_;

public:
  ParsedVertexAttributes() = default;
  MOVE_CONSTRUCTIBLE_ONLY(ParsedVertexAttributes);

  void add(std::string const& name, opengl::VertexAttribute&& va)
  {
    auto pair = std::make_pair(name, MOVE(va));
    pair_.emplace_back(MOVE(pair));
  }

  opengl::VertexAttribute get_copy_of_va(std::string const& va_name)
  {
    auto const cmp     = [&va_name](auto const& it) { return it.first == va_name; };
    auto       find_it = std::find_if(pair_.cbegin(), pair_.cend(), cmp);

    // TODO: for now assume we always find requested VA.
    assert(find_it != pair_.cend());
    return find_it->second.clone();
  }
};

Result<opengl::TextureTable, std::string>
load_textures(common::Logger& logger, CppTable const& table)
{
  opengl::TextureTable ttable;
  auto const           load_texture = [&logger,
                             &ttable](auto const& resource) -> Result<common::none_t, std::string> {
    auto const name = get_string_or_abort(resource, "name");
    auto const type = get_string_or_abort(resource, "type");

    auto const  wrap_s    = get_string(resource, "wrap").value_or("clamp");
    GLint const wrap_mode = texture::wrap_mode_from_string(wrap_s.c_str());

    auto const uv_max = get_float(resource, "uvs").value_or(1.0f);

    unsigned int const texture_unit = get_unsignedint(resource, "texture_unit").value_or(0);

    TextureInfo ti;
    ti.wrap   = wrap_mode;
    ti.uv_max = uv_max;

    auto const load_2dtexture = [&](GLenum const format) -> Result<common::none_t, std::string> {
      auto const               filename = get_string_or_abort(resource, "filename");
      opengl::TextureFilenames texture_names{name, {filename}};

      ti.format = format;
      Texture t = TRY_MOVEOUT(
          opengl::texture::upload_2d_texture(logger, texture_names.filenames[0], MOVE(ti)));

      ttable.add_texture(MOVE(texture_names), MOVE(t));
      return OK_NONE;
    };
    auto const load_3dtexture = [&](GLenum const format) -> Result<common::none_t, std::string> {
      auto const front  = get_string_or_abort(resource, "front");
      auto const right  = get_string_or_abort(resource, "right");
      auto const back   = get_string_or_abort(resource, "back");
      auto const left   = get_string_or_abort(resource, "left");
      auto const top    = get_string_or_abort(resource, "top");
      auto const bottom = get_string_or_abort(resource, "bottom");

      opengl::TextureFilenames texture_names{name, {front, right, back, left, top, bottom}};
      ti.format = format;

      Texture t = TRY_MOVEOUT(
          opengl::texture::upload_3dcube_texture(logger, texture_names.filenames, MOVE(ti)));

      ttable.add_texture(MOVE(texture_names), MOVE(t));
      return OK_NONE;
    };

    if (type == "texture:3dcube-RGB") {
      TRY_MOVEOUT(load_3dtexture(GL_RGB));
    }
    else if (type == "texture:3dcube-RGBA") {
      TRY_MOVEOUT(load_3dtexture(GL_RGBA));
    }
    else if (type == "texture:2d-RGBA") {
      TRY_MOVEOUT(load_2dtexture(GL_RGBA));
    }
    else if (type == "texture:2d-RGB") {
      TRY_MOVEOUT(load_2dtexture(GL_RGB));
    }
    else {
      // TODO: implement more.
      LOG_ERROR_SPRINTF("error, type is: %s", type);
      std::abort();
    }

    return OK_NONE;
  };

  auto const resource_table = get_table_array_or_abort(table, "resource");
  std::for_each(resource_table->begin(), resource_table->end(), load_texture);
  return OK_MOVE(ttable);
}

auto
load_fog(CppTable const& file)
{
  auto const& data     = get_first_and_only_entry(file, "fog");
  auto const  density  = get_float_or_abort(data, "density");
  auto const  gradient = get_float_or_abort(data, "gradient");
  auto const  color    = get_color_or_abort(data, "color");

  return Fog{density, gradient, color};
}

std::optional<NameMaterial>
load_material(CppTable const& file)
{
  // clang-format off
  auto const name      = get_string_or_abort(file, "name");
  auto const ambient   = MAKEOPT(get_vec3(file,  "ambient"));
  auto const diffuse   = MAKEOPT(get_vec3(file,  "diffuse"));
  auto const specular  = MAKEOPT(get_vec3(file,  "specular"));
  auto const shininess = MAKEOPT(get_float(file, "shininess"));
  // clang-format on

  Material const material{ambient, diffuse, specular, shininess};
  return std::make_optional(NameMaterial{name, material});
}

auto
load_material_or_abort(CppTable const& file)
{
  auto optional = load_material(file);
  if (!optional) {
    std::abort();
  }
  return *optional;
}

auto
load_attenuation(CppTable const& file)
{
  auto const name      = get_string_or_abort(file, "name");
  auto const constant  = get_float(file, "constant").value_or(0);
  auto const linear    = get_float(file, "linear").value_or(0);
  auto const quadratic = get_float(file, "quadratic").value_or(0);

  return NameAttenuation{name, Attenuation{constant, linear, quadratic}};
}

auto
load_attenuations(common::Logger& logger, CppTable const& table)
{
  auto const                   table_array = get_table_array(table, "attenuation");
  std::vector<NameAttenuation> result;
  for (auto const& it : *table_array) {
    result.emplace_back(load_attenuation(it));
  }
  return result;
}

auto
load_materials(common::Logger& logger, CppTable const& table)
{
  auto const table_array = get_table_array(table, "material");

  MaterialTable material_table;
  for (auto const& it : *table_array) {
    auto material = load_material_or_abort(it);
    material_table.add(MOVE(material));
  }
  return material_table;
}

void
load_entities(common::Logger& logger, CppTable const& level_table, LevelAssets& assets,
              EntityRegistry& registry)
{
  auto&       ttable       = assets.texture_table;
  auto const& mtable       = assets.material_table;
  auto const& attenuations = assets.attenuations;

  auto const load_entity = [&](auto const& file) {
    // clang-format off
    auto const name          = get_string(file,          "name").value_or("FromFileUnnamed");
    auto const shader        = get_string_or_abort(file, "shader");
    auto const geometry      = get_string_or_abort(file, "geometry");
    auto const pos           = get_vec3_or_abort(file,   "position");
    auto const scale_o       = get_vec3(file,            "scale");
    auto const rotation_o    = get_vec3(file,            "rotation");
    auto const color         = get_color(file,           "color");
    auto const material_o    = get_string(file,          "material");
    auto const texture_name  = get_string(file,          "texture");
    auto const is_hidden     = get_bool(file,            "hidden").value_or(false);
    bool const random_junk   = get_bool(file,            "random_junk_from_file").value_or(false);

    // sub-tables or "inner"-tables
    auto const orbital_o       = get_table(file, "orbital-body");
    auto const pointlight_o    = get_table(file, "pointlight");
    // clang-format on

    // texture OR color fields, not both
    assert((!color && !texture_name) || (!color && texture_name) || (color && !texture_name));

    auto eid = registry.create();
    registry.assign<Name>(eid, name);

    auto& transform       = registry.assign<Transform>(eid);
    transform.translation = pos;

    registry.assign<IsRenderable>(eid, is_hidden);
    registry.assign<ShaderName>(eid, shader);

    if (scale_o) {
      transform.scale = *scale_o;
    }
    if (rotation_o) {
      transform.rotate_xyz_degrees(*rotation_o);
    }

    if (random_junk) {
      registry.assign<JunkEntityFromFILE>(eid);
    }

    if (orbital_o) {
      auto const x = get_float_or_abort(orbital_o, "x");
      auto const y = get_float_or_abort(orbital_o, "y");
      auto const z = get_float_or_abort(orbital_o, "z");

      auto const radius = glm::vec3{x, y, z};
      auto const offset = get_float(orbital_o, "offset").value_or(0.0);
      registry.assign<OrbitalBody>(eid, radius, offset);
    }

    if (geometry == "cube") {
      auto&      cr              = registry.assign<CubeRenderable>(eid);
      auto const cube_vertices_o = get_table_or_abort(file, "cube_vertices");
      cr.min                     = get_vec3(cube_vertices_o, "min").value_or(glm::vec3{1.0f});
      cr.max                     = get_vec3(cube_vertices_o, "max").value_or(glm::vec3{1.0f});
    }
    else if (boost::starts_with(geometry, "mesh")) {
      auto const parse_meshname = [](auto const& field) {
        auto const len = ::strlen("mesh:");
        assert(0 < len);
        return field.substr(len, field.length() - len);
      };
      auto  mesh_name = parse_meshname(geometry);
      auto& meshc     = registry.assign<MeshRenderable>(eid, MOVE(mesh_name));
    }
    else if (boost::starts_with(geometry, "billboard")) {
      auto const parse_billboard = [](auto const& field) {
        auto const len = ::strlen("billboard:");
        assert(0 < len);
        auto const str = field.substr(len, field.length() - len);
        return Billboard::from_string(str);
      };
      auto& billboard = registry.assign<BillboardRenderable>(eid);
      billboard.value = parse_billboard(geometry);
    }
    if (color) {
      registry.assign<Color>(eid, *color);
    }
    if (texture_name) {
      LOG_DEBUG_SPRINTF("Looking up texture %s", *texture_name);
      auto& tr        = registry.assign<TextureRenderable>(eid);
      auto  texture_o = ttable.find(*texture_name);
      assert(texture_o);
      tr.texture_info = &*texture_o;
    }

    if (pointlight_o) {
      auto const attenuation = get_string_or_abort(pointlight_o, "attenuation");

      auto const cmp = [&](NameAttenuation const& na) { return na.name == attenuation; };
      auto const it  = std::find_if(attenuations.cbegin(), attenuations.cend(), cmp);
      assert(it != attenuations.cend());

      auto& pl       = registry.assign<PointLight>(eid);
      pl.attenuation = it->value;

      auto& light    = pl.light;
      // TODO: instead of converting the vec4 to vec3, just read it in as a vec3.
      light.diffuse  = get_color_or_abort(pointlight_o, "diffuse").rgb();
      light.specular = get_color_or_abort(pointlight_o, "specular").rgb();
    }

    auto& obj_store = assets.obj_store;
    if (common::cstrcmp(name.c_str(), "TreeLowpoly")) {
      auto& obj = obj_store.get(logger, registry.get<MeshRenderable>(eid).name);
      auto& tc  = registry.assign<TreeComponent>(eid, obj);
      tc.add_color(TreeColorType::Leaf, LOC4::GREEN);
      tc.add_color(TreeColorType::Leaf, LOC4::PINK);
      tc.add_color(TreeColorType::Trunk, LOC4::BROWN);
    }
    if (common::cstrcmp(name.c_str(), "Tree2")) {
      auto& obj = obj_store.get(logger, registry.get<MeshRenderable>(eid).name);
      auto& tc  = registry.assign<TreeComponent>(eid, obj);
      tc.add_color(TreeColorType::Leaf, LOC4::YELLOW);
      tc.add_color(TreeColorType::Stem, LOC4::RED);
      tc.add_color(TreeColorType::Stem, LOC4::BLUE);
      tc.add_color(TreeColorType::Trunk, LOC4::GREEN);
    }

    // An object receives light, if it has ALL ambient/diffuse/specular fields
    if (material_o) {
      auto const  material_name      = *material_o;
      auto const& material           = mtable.find(material_name);
      registry.assign<Material>(eid) = material;
    }
  };

  auto const entity_table = get_table_array(level_table, "entity");
  for (auto const& it : *entity_table) {
    load_entity(it);
  }
}

using LoadResult = Result<std::pair<std::string, opengl::ShaderProgram>, std::string>;
LoadResult
load_shader(common::Logger& logger, ParsedVertexAttributes& pvas, CppTable const& table)
{
  auto const name     = get_string_or_abort(table, "name");
  auto const vertex   = get_string_or_abort(table, "vertex");
  auto const fragment = get_string_or_abort(table, "fragment");
  auto const va_name  = get_string_or_abort(table, "va");

  // TODO: ugly hack, maybe think about...
  auto va      = pvas.get_copy_of_va(va_name);
  auto program = TRY_MOVEOUT(opengl::make_shader_program(logger, vertex, fragment, MOVE(va)));

  program.is_2d          = get_bool(table, "is_2d").value_or(false);
  program.instance_count = get_sizei(table, "instance_count");

  return Ok(std::make_pair(name, MOVE(program)));
}

Result<opengl::ShaderPrograms, std::string>
load_shaders(common::Logger& logger, ParsedVertexAttributes&& pvas, CppTable const& table)
{
  auto const             shaders_table = get_table_array_or_abort(table, "shaders");
  opengl::ShaderPrograms sps;
  for (auto const& shader_table : *shaders_table) {
    auto pair = TRY_MOVEOUT(load_shader(logger, pvas, shader_table));
    sps.add(pair.first, MOVE(pair.second));
  }
  return Ok(MOVE(sps));
}

struct TerrainData
{
  std::string mesh_name;
  int         terrain_index;
  int         water_index;
};

auto
load_vas(CppTable const& table)
{
  auto const table_array = table->get_table_array("vas");
  assert(table_array);

  auto const read_data = [&](auto const& table, char const* fieldname, size_t& index) {
    // THINKING EXPLAINED:
    //
    // If there isn't a field, bail early. However, if there IS a field, ensure it has the fields
    // we expect.
    TRY_OPTION(auto data_table, table->get_table(fieldname));
    auto const datatype_s = get_string_or_abort(data_table, "datatype");

    // TODO: FOR NOW, only support floats. Easy to implement rest
    assert("float" == datatype_s);
    auto const datatype = GL_FLOAT;
    auto const num      = get_or_abort<int>(data_table, "num");

    auto const                   uint_index     = static_cast<GLuint>(index);
    auto const                   attribute_type = attribute_type_from_string(fieldname);
    opengl::AttributePointerInfo api{uint_index, datatype, attribute_type, num};

    ++index;
    return cpptoml::option<opengl::AttributePointerInfo>{MOVE(api)};
  };

  auto const add_next_found = [&read_data](auto& apis, auto const& table, char const* fieldname,
                                           size_t& index) {
    auto       data_o    = read_data(table, fieldname, index);
    bool const data_read = !!data_o;
    if (data_read) {
      auto data = MOVE(*data_o);
      apis.emplace_back(MOVE(data));
    }
  };

  ParsedVertexAttributes pvas;
  auto const             fn = [&pvas, &add_next_found](auto const& table) {
    auto const name = get_string_or_abort(table, "name");

    size_t                                    i = 0u;
    std::vector<opengl::AttributePointerInfo> apis;
    add_next_found(apis, table, "position", i);
    add_next_found(apis, table, "normal", i);
    add_next_found(apis, table, "color", i);
    add_next_found(apis, table, "uv", i);

    pvas.add(name, make_vertex_attribute(apis));
  };
  std::for_each((*table_array).begin(), (*table_array).end(), fn);
  return pvas;
}

auto
load_global_lighting(CppTable const& table)
{
  auto const global_lighting = get_first_and_only_entry(table, "global-lighting");
  auto const ambient         = get_color_or_abort(global_lighting, "ambient").rgb();

  auto const directinal_table = get_table_or_abort(global_lighting, "directional");
  auto const diffuse          = get_color_or_abort(directinal_table, "diffuse").rgb();
  auto const specular         = get_color_or_abort(directinal_table, "specular").rgb();
  auto const direction        = get_vec3_or_abort(directinal_table, "direction");

  Light            light{diffuse, specular};
  DirectionalLight dlight{MOVE(light), direction};
  return GlobalLight{ambient, MOVE(dlight)};
}

} // namespace

namespace boomhs
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// LevelLoader
Result<LevelAssets, std::string>
LevelLoader::load_level(common::Logger& logger, EntityRegistry& registry,
                        std::string const& filename)
{
  CppTable engine_table = cpptoml::parse_file("engine.toml");
  assert(engine_table);

  ParsedVertexAttributes pvas = load_vas(engine_table);
  auto                   sps  = TRY_MOVEOUT(load_shaders(logger, MOVE(pvas), engine_table));

  CppTable resource_table = cpptoml::parse_file("levels/resources.toml");
  assert(resource_table);

  auto const mesh_table = get_table_array_or_abort(resource_table, "meshes");
  ObjStore   objstore =
      TRY_MOVEOUT(load_objfiles(logger, mesh_table).mapErrorMoveOut(loadstatus_to_string));

  LOG_TRACE("loading level data begin ...");
  LOG_TRACE("textures ...");
  auto texture_table = TRY_MOVEOUT(load_textures(logger, resource_table));

  LOG_TRACE("materials ...");
  auto material_table = load_materials(logger, resource_table);

  LOG_TRACE("attenuations ...");
  auto attenuations = load_attenuations(logger, resource_table);

  CppTable level_table = cpptoml::parse_file("levels/" + filename);
  assert(level_table);

  LOG_TRACE("global lighting ...");
  auto glight = load_global_lighting(level_table);

  LOG_TRACE("global fog ...");
  auto fog = load_fog(level_table);

  LOG_TRACE("loading level finished successfully!");
  LevelAssets assets{MOVE(glight),       MOVE(fog),           MOVE(material_table),
                     MOVE(attenuations),

                     MOVE(objstore),     MOVE(texture_table), MOVE(sps)};
  load_entities(logger, level_table, assets, registry);
  return OK_MOVE(assets);
}

} // namespace boomhs

ext: cxx

max_stars_repo_name: bjadamson/BoomHS

max_issues_repo_name: bjadamson/BoomHS

max_forks_repo_name: bjadamson/BoomHS


text:
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/CPPAlliance/url
//

// Test that header file is self-contained.
#include <boost/url/error.hpp>
#include <boost/url/grammar/error.hpp>

#include "test_suite.hpp"

#include <memory>

namespace boost {
namespace urls {

class error_test
{
public:
    void check(error e)
    {
        auto const ec = BOOST_URL_ERR(e);
        BOOST_TEST(ec.category().name() != nullptr);
        BOOST_TEST(! ec.message().empty());
        BOOST_TEST(ec.category().default_error_condition(
            static_cast<int>(e)).category() == ec.category());
    }

    void check(grammar::condition c, error e)
    {
        {
            auto const ec = BOOST_URL_ERR(e);
            BOOST_TEST(ec.category().name() != nullptr);
            BOOST_TEST(! ec.message().empty());
            BOOST_TEST(ec == c);
        }
        {
            auto ec = make_error_condition(c);
            BOOST_TEST(ec.category().name() != nullptr);
            BOOST_TEST(! ec.message().empty());
            BOOST_TEST(ec == c);
        }
    }

    void
    run()
    {
        check(error::bad_alpha);
        check(error::bad_digit);
        check(error::bad_empty_element);
        check(error::bad_hexdig);
        check(error::bad_ipv6);
        check(error::bad_leading_zero);
        check(error::bad_octet);
        check(error::bad_schemeless_path_segment);
        check(error::empty);
        check(error::empty_path_segment);
        check(error::illegal_null);
        check(error::illegal_reserved_char);
        check(error::invalid);
        check(error::missing_char_literal);
        check(error::missing_path_segment);
        check(error::missing_path_separator);
        check(error::missing_words);
        check(error::non_canonical);
        check(error::wrong_char_literal);

        check(grammar::condition::fatal, error::bad_pct_hexdig);
        check(grammar::condition::fatal, error::incomplete_pct_encoding);
        check(grammar::condition::fatal, error::missing_pct_hexdig);

        check(error::no_space);
        check(error::not_a_base);
    }
};

TEST_SUITE(error_test, "boost.url.error");

} // urls
} // boost

ext: cpp

max_stars_repo_name: vgrigoriu/url

max_issues_repo_name: vgrigoriu/url

max_forks_repo_name: vgrigoriu/url


text:
//          Copyright Carl Philipp Reh 2006 - 2019.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef SGE_SPRITE_INTRUSIVE_ORDERED_DETAIL_ITERATOR_BASE_IMPL_HPP_INCLUDED
#define SGE_SPRITE_INTRUSIVE_ORDERED_DETAIL_ITERATOR_BASE_IMPL_HPP_INCLUDED

#include <sge/sprite/intrusive/ordered/iterator_fwd.hpp>
#include <sge/sprite/intrusive/ordered/detail/range_iterator.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <iterator>
#include <type_traits>
#include <fcppt/config/external_end.hpp>

namespace sge::sprite::intrusive::ordered::detail
{

template <typename Choices, typename Order, bool IsConst>
struct iterator_base_impl
{
  using range_iterator_traits = std::iterator_traits<
      sge::sprite::intrusive::ordered::detail::range_iterator<Choices, IsConst>>;

  using type = boost::iterator_facade<
      sge::sprite::intrusive::ordered::iterator<Choices, Order, IsConst>,
      typename range_iterator_traits::value_type,
      std::forward_iterator_tag,
      typename range_iterator_traits::reference,
      typename range_iterator_traits::difference_type>;
};

}

#endif

ext: hpp

max_stars_repo_name: cpreh/spacegameengine

max_issues_repo_name: cpreh/spacegameengine

max_forks_repo_name: cpreh/spacegameengine


text:
#pragma once

#include <boost/serialization/array.hpp>
#include <boost/serialization/vector.hpp>

namespace cho {
namespace vis {

struct EventData {
  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive& ar, const unsigned int) {
    ar& dummy;
  }
  int dummy;
};
}  // namespace vis
}  // namespace cho


text:
//
// Created by leipeng on 2019-08-28.
//

#include <boost/fiber/fiber.hpp>
#include <boost/fiber/fss.hpp>
#include <thread>

void clean_fss_int(int* p) {
    printf("destroy int = %d\n", *p);
}
static boost::fibers::fiber_specific_ptr<int> fs0(clean_fss_int);
static thread_local boost::fibers::fiber_specific_ptr<int> fs1(clean_fss_int);

int main(int argc, char* argv[]) {
    boost::fibers::fiber_specific_ptr<int> fs2(clean_fss_int);
    auto func = [&](const char* name) {
        printf("---- %s ----\n", name);
        boost::fibers::fiber_specific_ptr<int> fs3(clean_fss_int);
        if (fs0.get() == NULL) {
            fs0.reset(new int(0));
        }
        if (fs1.get() == NULL) {
            fs1.reset(new int(1));
        }
        if (fs2.get() == NULL) {
            fs2.reset(new int(2));
        }
        if (fs3.get() == NULL) {
            fs3.reset(new int(3));
        }
    };
    std::thread thr(func, "thread");
    thr.join();
    boost::fibers::fiber fb(func, "fiber");
    fb.join();
    return 0;
}

max_stars_repo_name: rockeet/terark-zip

max_issues_repo_name: rockeet/terark-zip

max_forks_repo_name: rockeet/terark-zip


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

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/fourthrooteps.hpp>
#include <boost/simd/constant/eps.hpp>
#include <boost/simd/as.hpp>
#include <scalar_test.hpp>

STF_CASE_TPL( "Check fourthrooteps behavior for integral types"
            , (std::uint8_t)(std::uint16_t)(std::uint32_t)(std::uint64_t)
              (std::int8_t)(std::int16_t)(std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::fourthrooteps;
  using boost::simd::Fourthrooteps;

  STF_TYPE_IS(decltype(Fourthrooteps<T>()), T);
  STF_EQUAL(Fourthrooteps<T>(), T(1));
  STF_EQUAL(fourthrooteps( as(T{}) ),T(1));
}

STF_CASE_TPL( "Check fourthrooteps behavior for floating types"
            , (double)(float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::fourthrooteps;
  using boost::simd::Fourthrooteps;
  using boost::simd::Eps;

  STF_TYPE_IS(decltype(Fourthrooteps<T>()), T);
  auto z1 = Fourthrooteps<T>();
  STF_ULP_EQUAL(z1*z1*z1*z1, Eps<T>(), 1);
  auto z2 = fourthrooteps( as(T{}) );
  STF_ULP_EQUAL(z2*z2*z2*z2, Eps<T>(), 2);
}


text:
#define BOOST_TEST_MODULE Taskit
#define BOOST_TEST_DYN_LINK

#include "taskit.hpp"

#include <boost/test/unit_test.hpp>
#include <future>

#include <iostream>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>

class Ctx
{
public:

    void storeInfo(const std::string& s)
    {
        extractedInfo_ = s;
    }

    friend std::ostream& operator<<(std::ostream& os, const Ctx& ctx)
    {
        return os << ctx.extractedInfo_;
    }

private:

    std::string extractedInfo_;
};

struct A
{
    auto operator()(std::ostream& os, Ctx& ctx) const
    {
        ctx.storeInfo("A A A");
        os << " " << ctx << " ...";
        return 'A';
    }
};

struct B
{
    auto operator()(std::ostream& os, Ctx& ctx) const
    {
        ctx.storeInfo("BB BB BB");
        os << " " << ctx << " ...";
        return 'B';
    }
};

struct C
{
    auto operator()(std::ostream& os, Ctx& ctx) const
    {
        ctx.storeInfo("CCC CCC CCC");
        os << " " << ctx << " ...";
        return 'C';
    }
};

auto d(std::ostream& os, Ctx& ctx)
{
    ctx.storeInfo("dddd dddd dddd");
    os << " " << ctx << " ...";
    return 'd';
}

struct P
{
    std::string s_;

public:

    P(int times, const std::string s) : s_( s )
    {
        for(int i = 1; i < times; ++i) s_ += s;
    }

    auto operator()(std::ostream& os, Ctx& ctx) const
    {
        ctx.storeInfo( s_ );
        os << " " << ctx << " ...";
        return 'P';
    }
};

BOOST_AUTO_TEST_CASE( simple_sequence_test )
{
    using namespace std;

    char letter = 'e';
    auto e = [letter](std::ostream& os, Ctx& ctx)
    {
        ctx.storeInfo("eeeee eeeee eeeee");
        os << " " << ctx << " ...";
        return letter;
    };

    A a;

    std::stringstream res;

    cout << "Create TaskSequence..." << endl;
    auto parser = taskit::make_TaskSequence(
                                              taskit::make_TaskType( std::move( a ) ),
                                              taskit::make_TaskType( B{} ),
                                              taskit::make_TaskType<C>(),
                                              taskit::make_TaskType( std::move( d ) ),
                                              taskit::make_TaskType( std::move( e ) ),
                                              taskit::make_TaskType<P>(4, "PpPpPp")
                                             );

    Ctx ctx;

    auto ret = parser(res, ctx);

    std::cout << "'" << ret << "' was inspected: " << ctx << std::endl;

    std::cout << "Parsed info ==> " << res.str() << std::endl;
    BOOST_CHECK( res.str() == " A A A ... BB BB BB ... CCC CCC CCC ... dddd dddd dddd ... eeeee eeeee eeeee ... PpPpPpPpPpPpPpPpPpPpPpPp ..." );
}

BOOST_AUTO_TEST_CASE( simple_selector_test )
{
    using namespace std;

    char letter = 'e';
    auto e = [letter](std::ostream& os, Ctx& ctx)
    {
        ctx.storeInfo("eeeee eeeee eeeee");
        os << " " << ctx << " ...";
        return letter;
    };

    A a;

    constexpr const char stages[] = { 'A', 'B', 'C', 'd', 'e', 'P' };

    std::stringstream res;

    for( auto parser_selector : stages )
    {
        cout << "Create parser " << parser_selector << "..." << endl;
        auto parser = taskit::make_Task( parser_selector,
#if __cplusplus >= 201703L
                                           taskit::make_TaskType<'A'>( std::move( a ) ),
                                           taskit::make_TaskType<'B'>( B{} ),
                                           taskit::make_TaskType<'C', C>(),
                                           taskit::make_TaskType<'d'>( std::move( d ) ),
                                           taskit::make_TaskType<'e'>( std::move( e ) ),
                                           taskit::make_TaskType<'P', P>( 4, "PpPpPp"s )
#else
                                           taskit::make_TaskType<char, 'A'>( std::move( a ) ),
                                           taskit::make_TaskType<char, 'B'>( B{} ),
                                           taskit::make_TaskType<char, 'C', C>(),
                                           taskit::make_TaskType<char, 'd'>( std::move( d ) ),
                                           taskit::make_TaskType<char, 'e'>( std::move( e ) ),
                                           taskit::make_TaskType<char, 'P', P>( 4, "PpPpPp"s )
#endif
                                         );

        Ctx ctx;

        auto ret = parser(res, ctx);

        std::cout << "'" << ret << "' was inspected: " << ctx << std::endl;
        BOOST_CHECK( ret == parser_selector );
    }

    std::cout << "Parsed info ==> " << res.str() << std::endl;
    BOOST_CHECK( res.str() == " A A A ... BB BB BB ... CCC CCC CCC ... dddd dddd dddd ... eeeee eeeee eeeee ... PpPpPpPpPpPpPpPpPpPpPpPp ..." );
}

BOOST_AUTO_TEST_CASE( packedTask_test )
{
    char letter = 'e';
    auto e = [letter](std::ostream& os, Ctx& ctx)
    {
        ctx.storeInfo("eeeee eeeee eeeee");
        os << " " << ctx << " ...";
        return letter;
    };

    A a;

    constexpr const char stages[] = { 'A', 'B', 'C', 'd', 'e' };

    std::stringstream res;

    for( auto parser_selector : stages )
    {
        std::cout << "Create parser " << parser_selector << "..." << std::endl;
        auto parser = taskit::make_Task( parser_selector,
#if __cplusplus >= 201703L
                                           taskit::make_TaskType<'A'>( std::move( a ) ),
                                           taskit::make_TaskType<'B'>( B{} ),
                                           taskit::make_TaskType<'C', C>(),
                                           taskit::make_TaskType<'d'>( std::move( d ) ),
                                           taskit::make_TaskType<'e'>( std::move( e ) )
#else
                                           taskit::make_TaskType<char, 'A'>( std::move( a ) ),
                                           taskit::make_TaskType<char, 'B'>( B{} ),
                                           taskit::make_TaskType<char, 'C', C>(),
                                           taskit::make_TaskType<char, 'd'>( std::move( d ) ),
                                           taskit::make_TaskType<char, 'e'>( std::move( e ) )
#endif
                                         );

        Ctx ctx;
        std::packaged_task<char(std::ostream&, Ctx&)> task( std::bind(parser, std::ref(res), std::ref(ctx)) );
        std::future<char> result = task.get_future();

        task(res, ctx);

        auto ret = result.get();

        std::cout << "'" << ret << "' was inspected: " << ctx << std::endl;
        BOOST_CHECK( ret == parser_selector );
    }

    std::cout << "Parsed info ==> " << res.str() << std::endl;
    BOOST_CHECK( res.str() == " A A A ... BB BB BB ... CCC CCC CCC ... dddd dddd dddd ... eeeee eeeee eeeee ..." );
}

// The objects must have static storage duration and linkage (external pre C++11, internal or
// external in C++11), so that pointers to them can be created at compile time
std::string empty_s;
std::string you_s("you");
std::string the_s("the");
std::string gosen_s("Goʹshen");

BOOST_AUTO_TEST_CASE( dictionary_test )
{
    using namespace std;
    using namespace std::string_literals;

    ifstream in("./test/data.txt");

    using Dict_t = unordered_map<string, vector<string>>;

    Dict_t dict;

    auto wordExtractor = [] (const string& wordToBeExtracted, Dict_t& dict)
    {
        dict[wordToBeExtracted].emplace_back( wordToBeExtracted );
    };

    auto ignore = [] (const string& wordToBeExtracted, Dict_t& dict) {};

    string lookup;
    while( in >> lookup )
    {
        auto word_locator = taskit::make_Task( lookup,
                                                 taskit::make_TaskType<decltype(the_s)  , the_s  >( wordExtractor ),
                                                 taskit::make_TaskType<decltype(you_s)  , you_s  >( wordExtractor ),
                                                 taskit::make_TaskType<decltype(gosen_s), gosen_s>( wordExtractor ),
                                                 taskit::make_TaskType<decltype(empty_s), empty_s>( std::move(ignore) )
                                               );
        word_locator(lookup, dict);
    }

    vector<string> vec_result;
    for( const auto& p : dict )
    {
        for( const auto& s : p.second )
        {
            vec_result.push_back( s );
        }
    }

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

    stringstream result;
    for( const auto& s : vec_result )
    {
       result << s << " ";
    }
    cout << result.str() << endl;
    BOOST_CHECK( result.str() == "Goʹshen the the the you you ");
}

BOOST_AUTO_TEST_CASE( generate_tasks_before_selected_test )
{
    using namespace std;
    using namespace std::string_literals;

    ifstream in("./test/data.txt");

    using Dict_t = unordered_map<string, vector<string>>;

    Dict_t dict;

    auto wordExtractor = [] (const string& wordToBeExtracted, Dict_t& dict)
    {
        dict[wordToBeExtracted].emplace_back( wordToBeExtracted );
    };

    auto ignore = [] (const string& wordToBeExtracted, Dict_t& dict) {};

    string lookup;
    auto tasks = taskit::make_Tasks<decltype(lookup)>(
                                    taskit::make_TaskType<decltype(the_s)  , the_s  >( wordExtractor ),
                                    taskit::make_TaskType<decltype(you_s)  , you_s  >( wordExtractor ),
                                    taskit::make_TaskType<decltype(gosen_s), gosen_s>( wordExtractor ),
                                    taskit::make_TaskType<decltype(empty_s), empty_s>( std::move(ignore) ));
    while( in >> lookup )
    {
        const auto& word_locator = tasks.select( lookup );
        word_locator(lookup, dict);
    }

    vector<string> vec_result;
    for( const auto& p : dict )
    {
        for( const auto& s : p.second )
        {
            vec_result.push_back( s );
        }
    }

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

    stringstream result;
    for( const auto& s : vec_result )
    {
       result << s << " ";
    }
    cout << result.str() << endl;
    BOOST_CHECK( result.str() == "Goʹshen the the the you you ");
}

BOOST_AUTO_TEST_CASE( tasks_sizes_test )
{
    char parser_selector = 'A';
    auto parser = taskit::make_Task( parser_selector,
                                     taskit::make_TaskType<char, 'A', A>(),
                                     taskit::make_TaskType<char, 'B', B>(),
                                     taskit::make_TaskType<char, 'C', C>()
                                   );

    BOOST_CHECK( sizeof parser == 1 );
}

ext: cc

max_stars_repo_name: gosen/Taskit

max_issues_repo_name: gosen/Taskit

max_forks_repo_name: gosen/Taskit


text:
/**
 * Test suite for the @em null_archetype class.
 */

#include <duck/null_archetype.hpp>
#include <duck/assignable.hpp>
#include <duck/copy_constructible.hpp>
#include <duck/default_constructible.hpp>
#include <duck/destructible.hpp>

#include <boost/mpl/assert.hpp>

BOOST_MPL_ASSERT_NOT((duck::is_assignable<duck::null_archetype>));
BOOST_MPL_ASSERT_NOT((duck::is_copy_constructible<duck::null_archetype>));
BOOST_MPL_ASSERT_NOT((duck::is_default_constructible<duck::null_archetype>));
BOOST_MPL_ASSERT_NOT((duck::is_destructible<duck::null_archetype>));

max_stars_repo_name: ldionne/duck

max_issues_repo_name: ldionne/duck

max_forks_repo_name: ldionne/duck


text:
#pragma once
//=====================================================================//
/*!	@file
	@brief	モトローラーＳフォーマット入出力
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2017 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/glfw_app/blob/master/LICENSE
*/
//=====================================================================//
#include <vector>
#include <map>
#include <string>
#include <array>
#include "file_io.hpp"
#include <iomanip>
#include <boost/format.hpp>

namespace utils {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	Motolora Sx I/O クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class motsx_io {
	public:
		typedef std::array<uint8_t, 256> array;

		struct area_t {
			uint32_t	min_;
			uint32_t	max_;
			area_t(uint32_t min = 0xffffffff, uint32_t max = 0) : min_(min), max_(max) { }
		};
		typedef std::vector<area_t> areas;

		struct array_t {
			area_t	area_;
			array	array_;

			array_t() : area_(), array_() { array_.fill(0xff); }

			void set(uint32_t adr, uint8_t data) {
				if(area_.min_ > adr) area_.min_ = adr;
				if(area_.max_ < adr) area_.max_ = adr;
				array_[adr & 0xff] = data;
			}
		};

	private:
		area_t		area_;
		uint32_t	exec_;

		typedef std::map<uint32_t, array_t>	memory_map;

		memory_map	memory_map_;

		array		fill_array_;

		void write_byte_(uint32_t address, uint8_t val) {
			uint32_t base = address & 0xffff00;
			memory_map::iterator it = memory_map_.find(base);
			if(it == memory_map_.end()) {
				array_t t;
				t.set(address, val);
				memory_map_.emplace(base, t);
			} else {
				array_t& t = it->second;
				t.set(address, val);
			}
		}

		bool load_(utils::file_io& fio) {
			area_.min_ = 0xffffffff;
			area_.max_ = 0x00000000;

			uint32_t value = 0;
			uint32_t type = 0;
			uint32_t length = 0;
			uint32_t address = 0;
			uint32_t sum = 0;
			int vcnt = 0;

			bool toend = false;
			int mode = 0;

			while(1) {
				char ch;
				if(!fio.get_char(ch)) {
					break;
				}

			   	if(ch == ' ') {
			   	} else if(ch == 0x0d || ch == 0x0a) {
				   	if(toend) break;
			   	} else if(mode == 0 && ch == 'S') {
			   		mode = 1;
			   		value = vcnt = 0;
			   	} else if(ch >= '0' && ch <= '9') {
			   		value <<= 4;
			   		value |= ch - '0';
			   		++vcnt;
			   	} else if(ch >= 'A' && ch <= 'F') {
			   		value <<= 4;
			   		value |= ch - 'A' + 10;
			   		++vcnt;
			   	} else {
					std::cerr << "S format illegual character: '";
			   		if(ch >= 0x20 && ch <= 0x7f) {
						std::cerr << ch;
			   		} else {
						std::cerr << boost::format("0x%02X") % static_cast<int>(ch);
			   		}
					std::cerr << "'" << std::endl;
			   		return false;
			   	}

			   	if(mode == 1) {		// タイプ取得
			   		if(vcnt == 1) {
			   			type = value;
			   			mode = 2;
			   			value = vcnt = 0;
			   		}
			   	} else if(mode == 2) {	// レングス取得
			   		if(vcnt == 2) {
			   			length = value;
			   			sum = value;
			   			mode = 3;
			   			value = vcnt = 0;
			   		}
			   	} else if(mode == 3) {	// アドレス取得
			   		int alen = 0;
			   		if(type == 0) {
			   			alen = 4;
			   		} else if(type == 1) {
			   			alen = 4;
			   		} else if(type == 2) {
			   			alen = 6;
			   		} else if(type == 3) {
			   			alen = 8;
			   		} else if(type == 5) {
			   			alen = 4;
			   		} else if(type == 7) {
			   			alen = 8;
			   		} else if(type == 8) {
			   			alen = 6;
			   		} else if(type == 9) {
			   			alen = 4;
			   		} else {
			   			return false;
			   		}

			   		if(vcnt == alen) {
			   			address = value;
			   			if(type >= 1 && type <= 3) {
			   				if(area_.min_ > address) area_.min_ = address;
			   			}
			   			alen >>= 1;
			   			length -= alen;
			   			length -= 1;	// SUM の分サイズを引く
			   			while(alen > 0) {
			   				sum += value;
			   				value >>= 8;
			   				--alen;
			   			}
				   		if(type >= 1 && type <= 3) {
				   			mode = 4;
				   		} else if(type >= 7 && type <= 9) {
							exec_ = value;
				   			mode = 5;
				   		} else {
				   			mode = 4;
				   		}
				   		value = vcnt = 0;
				   	}
			   	} else if(mode == 4) {	// データ・レコード
			   		if(vcnt >= 2) {
			   			if(type >= 1 && type <= 3) {
			   				write_byte_(address, value);
			   				if(area_.max_ < address) area_.max_ = address;
			   				++address;
			   			}
			   			sum += value;
			   			value = vcnt = 0;
			   			--length;
			   			if(length == 0) {
			   				mode = 5;
			   			}
			   		}
			   	} else if(mode == 5) {	// SUM
			   		if(vcnt >= 2) {
			   			value &= 0xff;
			   			sum ^= 0xff;
			   			sum &= 0xff;
			   			if(sum != value) {	// SUM エラー
							std::cerr << "S format SUM error: ";
							std::cerr << boost::format("0x%02X -> %02X")
							    % static_cast<int>(value)
								% static_cast<int>(sum)
								<< std::endl;
			   				return false;
			   			} else {
			   				if(type >= 7 && type <= 9) {
			   					toend = true;
			   				}
			   				mode = 0;
			   				value = vcnt = 0;
			   			}
			   		}
			   	}
		   	}
		   	return true;
		}

		bool save_(utils::file_io& fio, const memory_map::value_type& m) {
			const array_t& a = m.second;
			fio.put_char('S');

			uint8_t sum = 0;
			uint32_t len = (a.area_.max_ - a.area_.min_ + 1) * 2;
			std::string adr;
			if(a.area_.max_ <= 0xffff) {
				adr = (boost::format("1%04X") % a.area_.max_).str();
				len += 4;
			} else if(a.area_.max_ <= 0xffffff) {
				adr = (boost::format("2%06X") % a.area_.max_).str();
				len += 6;
			} else {
				adr = (boost::format("3%08X") % a.area_.max_).str();
				len += 8;
			}
			len += 2; // for check sum
			fio.put((boost::format("%02X") % len).str());
			fio.put(adr);

			for(uint32_t i = a.area_.min_; i <= a.area_.max_; ++i) {
				uint8_t data = a.array_[i & 255];
				fio.put((boost::format("%02X") % data).str());
				sum += data;
			}

			fio.put((boost::format("%02X") % static_cast<uint32_t>(sum)).str());

			fio.put_char('\n');
			return false;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		motsx_io() : area_(), exec_(0x000000) {
			fill_array_.fill(0xff);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ロード
			@param[in]	path	ファイルパス
			@return エラー無しなら「true」
		*/
		//-----------------------------------------------------------------//
		bool load(const std::string& path) {
			utils::file_io fio;
			if(!fio.open(path, "rb")) {
				return false;
			}

			memory_map_.clear();

			if(!load_(fio)) {
				return false;
			}

			fio.close();

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	セーブ
			@param[in]	path	ファイルパス
			@return エラー無しなら「true」
		*/
		//-----------------------------------------------------------------//
		bool save(const std::string& path) {
			if(memory_map_.empty()) return false;

			utils::file_io fio;
			if(!fio.open(path, "wb")) {
				return false;
			}

			for(const auto& m : memory_map_) {
				if(!save_(fio, m)) {
					return false;
				}
			}

			fio.close();

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	メモリーへの書き込み
			@param[in]	address	アドレス
			@param[in]	data	データポインター
			@param[in]	len		長さ
		*/
		//-----------------------------------------------------------------//
		void write(uint32_t address, const uint8_t* data, uint32_t len) {
			for(uint32_t i = 0; i < len; ++i) {
				write_byte_(address + i, data[i]);
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	総ページ数の取得
			@return 総ページ数
		*/
		//-----------------------------------------------------------------//
		uint32_t get_total_page() const {
			return memory_map_.size();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	エリア・マップの作成
			@return エリア・マップ
		*/
		//-----------------------------------------------------------------//
		areas create_area_map() const {
			areas as;
			for(const auto& m : memory_map_) {
				if(as.empty()) {
					as.emplace_back(m.second.area_);
				} else {
					if((as.back().max_ + 1) == m.second.area_.min_) {
						as.back().max_ = m.second.area_.max_;
					} else {
						as.emplace_back(m.second.area_);
					}
				}
			}
			return as;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	エリア・マップの表示
			@param[in]	head	追加の文字列
		*/
		//-----------------------------------------------------------------//
		void list_area_map(const std::string& head) const {
			std::cout << head << boost::format("Motolola Sx format load map: (exec: 0x%08X)") % exec_;
			std::cout << std::endl;

			auto as = create_area_map();
			uint32_t total = 0;
			for(const auto& a : as) {
				auto n = a.max_ - a.min_ + 1;
				std::cout << head << boost::format("  0x%08X to 0x%08X (%d bytes)") % a.min_ % a.max_ % n;
				std::cout << std::endl;
				total += n;
			}
			std::cout << head << boost::format("  Total (%d bytes)") % total << std::endl << std::flush;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	エリアの取得
			@return エリア
		*/
		//-----------------------------------------------------------------//
		const area_t& get_area() const { return area_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	実行アドレスの取得
			@return 実行アドレス
		*/
		//-----------------------------------------------------------------//
		uint32_t get_exec() const { return exec_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	利用されているページを探す（有効なページ）
			@param[in]	address	アドレス
			@return 有効なページがあれば「true」
		*/
		//-----------------------------------------------------------------//
		bool find_page(uint32_t address) const {
			return memory_map_.find(address & 0xffff00) != memory_map_.end();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ページメモリーの取得
			@param[in]	address	ベースとなるアドレス
			@return ページメモリー @n
					無効なページの場合、内部データは全て 0xff となっている。
		*/
		//-----------------------------------------------------------------//
		const array& get_memory(uint32_t address) const {
			memory_map::const_iterator cit = memory_map_.find(address & 0xffff00);
			if(cit == memory_map_.end()) {
				return fill_array_;
			}
			return cit->second.array_;
		}
	};
}


text:
///////////////////////////////////////////////////////////////////////////////
// statistics::survival::data::data::mngr::include.hpp                       //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_SURVIVAL_DATA_DATA_MNGR_INCLUDE_HPP_ER_2009
#define BOOST_STATISTICS_SURVIVAL_DATA_DATA_MNGR_INCLUDE_HPP_ER_2009

#include <boost/statistics/survival/data/data/mngr/default_covariates.hpp>
#include <boost/statistics/survival/data/data/mngr/default_covariates_model.hpp>
#include <boost/statistics/survival/data/data/mngr/default_parameter_records.hpp>
#include <boost/statistics/survival/data/data/mngr/records.hpp>
#include <boost/statistics/survival/data/data/mngr/responses.hpp>

#endif


text:
/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */

#include <sstream>
#include <boost/assign/list_of.hpp>

#include "base/test/task_test_util.h"
#include "bgp/bgp_factory.h"
#include "bgp/bgp_session_manager.h"
#include "bgp/bgp_xmpp_channel.h"
#include "bgp/security_group/security_group.h"
#include "bgp/test/bgp_server_test_util.h"
#include "bgp/xmpp_message_builder.h"
#include "control-node/control_node.h"
#include "control-node/test/network_agent_mock.h"
#include "io/test/event_manager_test.h"
#include "schema/xmpp_unicast_types.h"
#include "testing/gunit.h"
#include "xmpp/xmpp_factory.h"
#include "xmpp/xmpp_server.h"

using namespace std;
using boost::assign::list_of;

static const char *config_2_control_nodes = "\
<config>\
    <bgp-router name=\'X\'>\
        <identifier>192.168.0.1</identifier>\
        <address>127.0.0.1</address>\
        <port>%d</port>\
        <session to=\'Y\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <bgp-router name=\'Y\'>\
        <identifier>192.168.0.2</identifier>\
        <address>127.0.0.2</address>\
        <port>%d</port>\
        <session to=\'X\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <virtual-network name='blue'>\
        <network-id>1</network-id>\
    </virtual-network>\
    <routing-instance name='blue'>\
        <virtual-network>blue</virtual-network>\
        <vrf-target>target:1:1</vrf-target>\
    </routing-instance>\
</config>\
";

static const char *config_2_control_nodes_no_vn_info = "\
<config>\
    <bgp-router name=\'X\'>\
        <identifier>192.168.0.1</identifier>\
        <address>127.0.0.1</address>\
        <port>%d</port>\
        <session to=\'Y\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <bgp-router name=\'Y\'>\
        <identifier>192.168.0.2</identifier>\
        <address>127.0.0.2</address>\
        <port>%d</port>\
        <session to=\'X\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <routing-instance name='blue'>\
        <vrf-target>target:1:1</vrf-target>\
    </routing-instance>\
</config>\
";

static const char *config_2_control_nodes_different_asn = "\
<config>\
    <bgp-router name=\'X\'>\
        <autonomous-system>64511</autonomous-system>\
        <identifier>192.168.0.1</identifier>\
        <address>127.0.0.1</address>\
        <port>%d</port>\
        <session to=\'Y\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <bgp-router name=\'Y\'>\
        <autonomous-system>64512</autonomous-system>\
        <identifier>192.168.0.2</identifier>\
        <address>127.0.0.2</address>\
        <port>%d</port>\
        <session to=\'X\'>\
            <address-families>\
                <family>route-target</family>\
                <family>inet-vpn</family>\
            </address-families>\
        </session>\
    </bgp-router>\
    <virtual-network name='blue'>\
        <network-id>1</network-id>\
    </virtual-network>\
    <routing-instance name='blue'>\
        <virtual-network>blue</virtual-network>\
        <vrf-target>target:1:1</vrf-target>\
    </routing-instance>\
</config>\
";

//
// Control Nodes X and Y.
// Agents A and B.
//
class BgpXmppInetvpn2ControlNodeTest : public ::testing::Test {
protected:
    BgpXmppInetvpn2ControlNodeTest()
        : thread_(&evm_), xs_x_(NULL), xs_y_(NULL) {
    }

    virtual void SetUp() {
        bs_x_.reset(new BgpServerTest(&evm_, "X"));
        bs_x_->session_manager()->Initialize(0);
        LOG(DEBUG, "Created BGP server at port: " <<
            bs_x_->session_manager()->GetPort());
        xs_x_ = new XmppServer(&evm_, test::XmppDocumentMock::kControlNodeJID);
        xs_x_->Initialize(0, false);
        LOG(DEBUG, "Created XMPP server at port: " <<
            xs_x_->GetPort());
        cm_x_.reset(new BgpXmppChannelManager(xs_x_, bs_x_.get()));

        bs_y_.reset(new BgpServerTest(&evm_, "Y"));
        bs_y_->session_manager()->Initialize(0);
        LOG(DEBUG, "Created BGP server at port: " <<
            bs_y_->session_manager()->GetPort());
        xs_y_ = new XmppServer(&evm_, test::XmppDocumentMock::kControlNodeJID);
        xs_y_->Initialize(0, false);
        LOG(DEBUG, "Created XMPP server at port: " <<
            xs_y_->GetPort());
        cm_y_.reset(new BgpXmppChannelManager(xs_y_, bs_y_.get()));

        thread_.Start();
    }

    virtual void TearDown() {
        xs_x_->Shutdown();
        task_util::WaitForIdle();
        bs_x_->Shutdown();
        task_util::WaitForIdle();
        cm_x_.reset();

        xs_y_->Shutdown();
        task_util::WaitForIdle();
        bs_y_->Shutdown();
        task_util::WaitForIdle();
        cm_y_.reset();

        TcpServerManager::DeleteServer(xs_x_);
        xs_x_ = NULL;
        TcpServerManager::DeleteServer(xs_y_);
        xs_y_ = NULL;

        if (agent_a_) { agent_a_->Delete(); }
        if (agent_b_) { agent_b_->Delete(); }

        evm_.Shutdown();
        thread_.Join();
        task_util::WaitForIdle();
    }

    void Configure(BgpServerTestPtr server, const char *cfg_template) {
        char config[4096];
        snprintf(config, sizeof(config), cfg_template,
                 server->session_manager()->GetPort());
        server->Configure(config);
    }

    void Configure(const char *cfg_template = config_2_control_nodes) {
        char config[4096];
        snprintf(config, sizeof(config), cfg_template,
                 bs_x_->session_manager()->GetPort(),
                 bs_y_->session_manager()->GetPort());
        bs_x_->Configure(config);
        bs_y_->Configure(config);
    }

    bool CheckEncap(autogen::TunnelEncapsulationListType &rt_encap,
        const string &encap) {
        if (rt_encap.tunnel_encapsulation.size() != 1)
            return false;
        return (rt_encap.tunnel_encapsulation[0] == encap);
    }

    bool CheckRoute(test::NetworkAgentMockPtr agent, string net,
        string prefix, string nexthop, int local_pref, const string &encap,
        const string &origin_vn, const vector<int> sgids) {
        const autogen::ItemType *rt = agent->RouteLookup(net, prefix);
        if (!rt)
            return false;
        if (rt->entry.next_hops.next_hop[0].address != nexthop)
            return false;
        if (local_pref && rt->entry.local_preference != local_pref)
            return false;
        if (!origin_vn.empty() && rt->entry.virtual_network != origin_vn)
            return false;
        if (!sgids.empty() &&
            rt->entry.security_group_list.security_group != sgids)
            return false;

        autogen::TunnelEncapsulationListType rt_encap =
            rt->entry.next_hops.next_hop[0].tunnel_encapsulation_list;
        if (!encap.empty() && !CheckEncap(rt_encap, encap))
            return false;
        return true;
    }

    void VerifyRouteExists(test::NetworkAgentMockPtr agent, string net,
        string prefix, string nexthop, int local_pref,
        const string &encap = string(), const string &origin_vn = string(),
        const vector<int> sgids = vector<int>()) {
        TASK_UTIL_EXPECT_TRUE(CheckRoute(
            agent, net, prefix, nexthop, local_pref, encap, origin_vn, sgids));
    }

    void VerifyRouteExists(test::NetworkAgentMockPtr agent, string net,
        string prefix, string nexthop, const vector<int> sgids) {
        TASK_UTIL_EXPECT_TRUE(CheckRoute(
            agent, net, prefix, nexthop, 0, string(), string(), sgids));
    }

    void VerifyRouteNoExists(test::NetworkAgentMockPtr agent, string net,
        string prefix) {
        TASK_UTIL_EXPECT_TRUE(agent->RouteLookup(net, prefix) == NULL);
    }

    EventManager evm_;
    ServerThread thread_;
    BgpServerTestPtr bs_x_;
    BgpServerTestPtr bs_y_;
    XmppServer *xs_x_;
    XmppServer *xs_y_;
    test::NetworkAgentMockPtr agent_a_;
    test::NetworkAgentMockPtr agent_b_;
    boost::scoped_ptr<BgpXmppChannelManager> cm_x_;
    boost::scoped_ptr<BgpXmppChannelManager> cm_y_;
};

//
// Added route has expected local preference.
//
TEST_F(BgpXmppInetvpn2ControlNodeTest, RouteAddLocalPref) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1", 200);
    task_util::WaitForIdle();

    // Verify that route showed up on agents A and B.
    VerifyRouteExists(agent_a_, "blue", route_a.str(), "192.168.1.1", 200);
    VerifyRouteExists(agent_b_, "blue", route_a.str(), "192.168.1.1", 200);

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

//
// Changed route has expected local preference.
//
TEST_F(BgpXmppInetvpn2ControlNodeTest, RouteChangeLocalPref) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1", 200);
    task_util::WaitForIdle();

    // Verify that route showed up on agents A and B.
    VerifyRouteExists(agent_a_, "blue", route_a.str(), "192.168.1.1", 200);
    VerifyRouteExists(agent_b_, "blue", route_a.str(), "192.168.1.1", 200);

    // Change route from agent A.
    agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1", 300);
    task_util::WaitForIdle();

    // Verify that route is updated up on agents A and B.
    VerifyRouteExists(agent_a_, "blue", route_a.str(), "192.168.1.1", 300);
    VerifyRouteExists(agent_b_, "blue", route_a.str(), "192.168.1.1", 300);

    // Change route from agent A so that it falls back to default local pref.
    agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1");
    task_util::WaitForIdle();

    // Verify that route is updated up on agents A and B.
    VerifyRouteExists(agent_a_, "blue", route_a.str(), "192.168.1.1", 100);
    VerifyRouteExists(agent_b_, "blue", route_a.str(), "192.168.1.1", 100);

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

//
// Agent flaps a route by changing it repeatedly.
//
TEST_F(BgpXmppInetvpn2ControlNodeTest, RouteFlap1) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A and change it repeatedly.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    for (int idx = 0; idx < 1024; ++idx) {
        agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1");
        agent_a_->AddRoute("blue", route_a.str(), "192.168.1.2");
    }

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

//
// Agent flaps a route by adding and deleting it repeatedly.
//
TEST_F(BgpXmppInetvpn2ControlNodeTest, RouteFlap2) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add and delete route from agent A repeatedly.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    for (int idx = 0; idx < 1024; ++idx) {
        agent_a_->AddRoute("blue", route_a.str(), "192.168.1.1");
        usleep(5000);
        agent_a_->DeleteRoute("blue", route_a.str());
    }

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

TEST_F(BgpXmppInetvpn2ControlNodeTest, TunnelEncap) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    test::NextHops next_hops;
    test::NextHop next_hop("192.168.1.1", 0, "udp");
    next_hops.push_back(next_hop);
    agent_a_->AddRoute("blue", route_a.str(), next_hops, 100);
    task_util::WaitForIdle();

    // Verify that route showed up on agents A and B.
    VerifyRouteExists(
        agent_a_, "blue", route_a.str(), "192.168.1.1", 100, "udp");
    VerifyRouteExists(
        agent_b_, "blue", route_a.str(), "192.168.1.1", 100, "udp");

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

TEST_F(BgpXmppInetvpn2ControlNodeTest, VirtualNetworkIndexChange) {
    Configure(config_2_control_nodes_no_vn_info);
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    test::NextHops next_hops;
    test::NextHop next_hop("192.168.1.1", 0, "udp");
    next_hops.push_back(next_hop);
    agent_a_->AddRoute("blue", route_a.str(), next_hops, 100);
    task_util::WaitForIdle();

    // Verify the origin VN on agents A and B.
    VerifyRouteExists(
        agent_a_, "blue", route_a.str(), "192.168.1.1", 100, "udp", "blue");
    VerifyRouteExists(
        agent_b_, "blue", route_a.str(), "192.168.1.1", 100, "udp", "unresolved");

    // Update the config to include VN index.
    Configure(config_2_control_nodes);
    task_util::WaitForIdle();

    // Verify the origin VN on agents A and B.
    VerifyRouteExists(
        agent_a_, "blue", route_a.str(), "192.168.1.1", 100, "udp", "blue");
    VerifyRouteExists(
        agent_b_, "blue", route_a.str(), "192.168.1.1", 100, "udp", "blue");

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

TEST_F(BgpXmppInetvpn2ControlNodeTest, SecurityGroupsSameAsn) {
    Configure();
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    test::NextHops next_hops;
    test::NextHop next_hop("192.168.1.1");
    next_hops.push_back(next_hop);
    vector<int> sgids = list_of
        (SecurityGroup::kMaxGlobalId - 1)(SecurityGroup::kMaxGlobalId + 1)
        (SecurityGroup::kMaxGlobalId - 2)(SecurityGroup::kMaxGlobalId + 2);
    test::RouteAttributes attributes(sgids);
    agent_a_->AddRoute("blue", route_a.str(), next_hops, attributes);
    task_util::WaitForIdle();

    // Verify that route showed up on agents A and B with expected sgids.
    sort(sgids.begin(), sgids.end());
    VerifyRouteExists(
        agent_a_, "blue", route_a.str(), "192.168.1.1", sgids);
    VerifyRouteExists(
        agent_b_, "blue", route_a.str(), "192.168.1.1", sgids);

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

TEST_F(BgpXmppInetvpn2ControlNodeTest, SecurityGroupsDifferentAsn) {
    Configure(config_2_control_nodes_different_asn);
    task_util::WaitForIdle();

    // Create XMPP Agent A connected to XMPP server X.
    agent_a_.reset(
        new test::NetworkAgentMock(&evm_, "agent-a", xs_x_->GetPort(),
            "127.0.0.1", "127.0.0.1"));
    TASK_UTIL_EXPECT_TRUE(agent_a_->IsEstablished());

    // Create XMPP Agent B connected to XMPP server Y.
    agent_b_.reset(
        new test::NetworkAgentMock(&evm_, "agent-b", xs_y_->GetPort(),
            "127.0.0.2", "127.0.0.2"));
    TASK_UTIL_EXPECT_TRUE(agent_b_->IsEstablished());

    // Register to blue instance
    agent_a_->Subscribe("blue", 1);
    agent_b_->Subscribe("blue", 1);

    // Add route from agent A.
    stringstream route_a;
    route_a << "10.1.1.1/32";
    test::NextHops next_hops;
    test::NextHop next_hop("192.168.1.1");
    next_hops.push_back(next_hop);
    vector<int> sgids = list_of
        (SecurityGroup::kMaxGlobalId - 1)(SecurityGroup::kMaxGlobalId + 1)
        (SecurityGroup::kMaxGlobalId - 2)(SecurityGroup::kMaxGlobalId + 2);
    test::RouteAttributes attributes(sgids);
    agent_a_->AddRoute("blue", route_a.str(), next_hops, attributes);
    task_util::WaitForIdle();

    // Verify that route showed up on agents A and B with expected sgids.
    vector<int> global_sgids = list_of
        (SecurityGroup::kMaxGlobalId - 1)(SecurityGroup::kMaxGlobalId - 2);
    sort(sgids.begin(), sgids.end());
    sort(global_sgids.begin(), global_sgids.end());
    VerifyRouteExists(
        agent_a_, "blue", route_a.str(), "192.168.1.1", sgids);
    VerifyRouteExists(
        agent_b_, "blue", route_a.str(), "192.168.1.1", global_sgids);

    // Delete route from agent A.
    agent_a_->DeleteRoute("blue", route_a.str());
    task_util::WaitForIdle();

    // Verify that route is deleted at agents A and B.
    VerifyRouteNoExists(agent_a_, "blue", route_a.str());
    VerifyRouteNoExists(agent_b_, "blue", route_a.str());

    // Close the sessions.
    agent_a_->SessionDown();
    agent_b_->SessionDown();
}

class TestEnvironment : public ::testing::Environment {
    virtual ~TestEnvironment() { }
    virtual void SetUp() {
    }
    virtual void TearDown() {
    }
};

static void SetUp() {
    ControlNode::SetDefaultSchedulingPolicy();
    BgpServerTest::GlobalSetUp();
    BgpObjectFactory::Register<StateMachine>(
        boost::factory<StateMachineTest *>());
    BgpObjectFactory::Register<BgpXmppMessageBuilder>(
        boost::factory<BgpXmppMessageBuilder *>());
    XmppObjectFactory::Register<XmppStateMachine>(
        boost::factory<XmppStateMachineTest *>());
}
static void TearDown() {
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    scheduler->Terminate();
}

int main(int argc, char **argv) {
    bgp_log_test::init();
    ::testing::InitGoogleTest(&argc, argv);
    ::testing::AddGlobalTestEnvironment(new TestEnvironment());
    SetUp();
    int result = RUN_ALL_TESTS();
    TearDown();

    return result;
}