diff --git a/.gitignore b/.gitignore index 222626c..17f394f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,12 @@ bin/ obj/ Debug/ Release/ +/.idea +*.cbp *.depend *.layout *.mk *.project Graph.txt -Makefile \ No newline at end of file +Makefile +CMakeLists.txt \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a07b5a..bcdd56b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,31 @@ +#Contributing to this repository + +首先,感谢对这个项目进行贡献的人. + +###简介 +Graph是一个强调封装与资源安全的图的实现.用户不必为了使用这个图而显式地动态分配内存. + +用户也难以直接得到图内部的结构的指针从而做出一些令人意想不到的事情. + +例如: + + directed_graph graph; + vertex_id v0 = graph.add_vertex(); + vertex_id v1 = graph.add_vertex(); + edge_id e0 = graph.add_edge(v0,v1); + graph.value(v0) = "v0"; + graph.vaue(e0) = "e0"; + graph.remove_vertex(v1); + +用户完全通过`vertex_id`和`edge_id`来和图进行交互.用户也没有访问权限来*非拷贝/移动地创建*和修改`vertex_id`和`edge_id`类型的对象.这将有助于封装. +###贡献方式 +* 提出改进意见(欢迎!). +* 报告bug. +* 提出增强功能. +* 重构. +* 实现新的功能. +* 修补bug(万分感激). + ###编码规范 以下规则只是建议各位遵守,若无暇顾及或觉得繁琐也可无视掉. 若觉得有不妥之处请联系我. @@ -7,20 +35,21 @@ #####缩进 内层代码应当缩进一个4个空格. #####花括号 -所有scope的起始花括号应当放在独立的新的一行,与上一行对齐;下一行开始内层代码(缩进一个tab). +所有scope的起始花括号应当放在独立的新的一行,与上一行对齐;下一行开始内层代码(缩进4个空格). scope的结束花括号应当放在独立的新的一行,与对应的起始花括号对齐. void func() { std::cout << "This is code.\n"; } + #####类 -类的所有类型别名的声明均放在类成员的最前面,并使用using关键字而不是typedef. +类的所有类型别名的声明均放在类成员的最前面,并使用alias-declaration(using关键字)而不是typedef declaration. 其余的成员按 public protected private的顺序出现,其中成员变量尽量放在成员函数之后. #####指针,引用与const 最底层的const放置在右侧. -有const时,const右侧的*或&向左与const贴紧. -无const时,*或&向右与标识符贴紧. +有const时,const右侧的 * 或&向左与const贴紧. +无const时, * 或&靠拢方向任意. 例子如下: int *p0 = nullptr; diff --git a/Graph.cbp b/Graph.cbp deleted file mode 100644 index 6d0f876..0000000 --- a/Graph.cbp +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - diff --git a/README.md b/README.md index 14fe355..cc6c4d7 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,26 @@ # Graph -这个项目提供 数据结构--图 的一个实现,同时提供一部分图的算法.它被设计为在安全与效率中取得平衡. +这个项目提供 数据结构--[图](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)) 的一个实现,同时提供一部分图的算法. -目前源文件为./test/test.cpp ,头文件在./include下. +Graph强调封装与资源安全. -该项目目前有一个表示无向图的模板类:lzhlib::undirected_garph. +目前测试文件在./test下 ,头文件在./include下. +###接口简介 +该项目目前有两个表示图的模板类:`lzhlib::undirected_graph`和`lzhlib::directed_graph`,分别表示无向图和有向图. +####`lzhlib::undirected_garph`. +这是一个表示无向图的模板类. 该类接受两个模板参数,分别是图中顶点和边存储的数据的类型. 例: - lzhlib::undirected_graph graph; + undirected_graph g; vertex_id v0 = g.add_vertex("v0"); vertex_id v1 = g.add_vertex("v1"); { - edge_id e0 = g.add_edge(v0, v1); - assert(g.value(e0) == ""); - g.value(e0) = "e0"; + edge_id e0 = g.add_edge(v0, v1,"e0"); + assert(g.get_edge(v0, v1) == e0); + assert(g.associated(v0, e0)); + assert(g.associated(v1, e0)); assert(g.adjacent(v0, v1)); assert(g.adjacent(v1, v0)); assert(g.get_edge(v0, v1) == e0); @@ -23,31 +28,93 @@ assert(g.value(v0) == "v0"); assert(g.value(v1) == "v1"); assert(g.value(e0) == "e0"); - - g.remove_edge(e0); + { + auto vertex = g.neighbors(v0); + assert(vertex.size() == 1); + assert(vertex[0] == v1); + } + { + auto vertex = g.neighbors(v1); + assert(vertex.size() == 1); + assert(vertex[0] == v0); + } + { + auto vertices = g.associated_vertices(e0); + assert(vertices.first == v0 && vertices.second == v1); + vector vertices0 = {vertices.first, vertices.second}; + vector vertices1; + for (auto v = g.first_vertex(); !g.vertex_end(v); v = g.next_vertex(v)) + vertices1.push_back(v); + assert(vertices0 == vertices1); + } + g.remove_edge(e0);//可选 + assert(!g.associated(v0, e0)); + assert(!g.associated(v1, e0)); assert(!g.adjacent(v0, v1)); assert(!g.adjacent(v1, v0)); } + g.remove_vertex(v0);//可选 + g.remove_vertex(v1);//可选 + +####`directed_graph` +这个模板类表示有向图. + +模板参数的意义与`undirected_graph`的相同. + +例: + + directed_graph g; + vertex_id v0 = g.add_vertex("v0"); + vertex_id v1 = g.add_vertex("v1"); { - edge_id e1 = g.add_edge(v0, v1, "e1"); - assert(g.value(e1) == "e1"); + edge_id e0 = g.add_edge(v0, v1); //不提供值初始化std::string(可以选择有) + assert(g.value(e0) == ""); //因而e0关联的值为空字符串 + assert(g.get_edge(v0, v1) == e0); + assert(g.associated(v0, e0)); + assert(!g.associated(v1, e0)); assert(g.adjacent(v0, v1)); - assert(g.adjacent(v1, v0)); - assert(g.get_edge(v0, v1) == e1); - assert(g.get_edge(v1, v0) == e1); + assert(!g.adjacent(v1, v0)); + assert(g.get_edge(v0, v1) == e0); + assert(g.value(v0) == "v0"); + assert(g.value(v1) == "v1"); + g.value(e0) = "e0"; + assert(g.value(e0) == "e0"); + { + auto vertices = g.neighbors(v0); + assert(vertices.size() == 1); + assert(vertices[0] == v1); + } + { + auto vertices = g.neighbors(v1); + assert(vertices.empty()); + } - g.remove_edge(e1); + { + auto vertices = g.associated_vertices(e0); + assert(vertices.first == v0 && vertices.second == v1); + vector vertices0 = {vertices.first, vertices.second}; + vector vertices1; + for (auto v = g.first_vertex(); !g.vertex_end(v); v = g.next_vertex(v)) //遍历所有vertex + vertices1.push_back(v); + assert(vertices0 == vertices1); + } + g.remove_edge(e0); //可选 + assert(!g.associated(v0, e0)); + assert(!g.associated(v1, e0)); assert(!g.adjacent(v0, v1)); assert(!g.adjacent(v1, v0)); } + g.remove_vertex(v0); //可选 + g.remove_vertex(v1); //可选 + +###要求: +* 使用支持C++14 或以上的编译器 -要求: +* 使编译器搜索头文件时能在Graph文件夹所在的父目录下搜索(添加编译选项,或修改环境变量等等),否则可能找不到头文件. -使用GCC 支持C++14 或以上的编译器. -环境变量Path包含Graph所在的父目录的路径.(否则可能找不到头文件) -贡献: +###贡献: 期望各位为此供献出自己的力量.更多信息参见 [CONTRIBUTING](./CONTRIBUTING.md)和 [TODO](./TODO.md). diff --git a/TODO.md b/TODO.md index df1f9b3..a089651 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,8 @@ ###TODO -1. 重构.永远欢迎重构. -2. 完成 directed_graph .(已在 branch directed_graph 中开始) -3. 讨论添加新的功能 -4. 利用已有功能添加算法. +* 重构.永远欢迎重构. +* ~~完成directed_graph.~~(已在 branch directed_graph 中~~开始~~结束). +* 改进repository,提高其效率. +* 讨论添加新的功能 +* 利用已有功能添加算法. +* 以wiki页或其它方式补充文档. diff --git a/images/class_diagram.jpg b/images/class_diagram.jpg new file mode 100644 index 0000000..9b084e7 Binary files /dev/null and b/images/class_diagram.jpg differ diff --git a/images/file_include.jpg b/images/file_include.jpg new file mode 100644 index 0000000..27b0b94 Binary files /dev/null and b/images/file_include.jpg differ diff --git a/include/Graph.h b/include/Graph.h deleted file mode 100644 index f2c5d71..0000000 --- a/include/Graph.h +++ /dev/null @@ -1,475 +0,0 @@ -#ifndef GRAPH_H_INCLUDED -#define GRAPH_H_INCLUDED -#include "Graph/include/repository.h" -#include -#include -#include -#include -#include -/** \file - * \brief 本文件提供与 [图](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) 有关的抽象. - * - * ##图 支持的操作:(摘自维基百科) -
    -
  • \code adjacent(G, x, y) \endcode tests whether there is an edge from the vertices x to y;
  • -
  • \code neighbors (G, x) \endcode lists all vertices y such that there is an edge from the vertices x to y;
  • -
  • \code add_vertex(G, x) \endcode adds the vertex x, if it is not there;
  • -
  • \code remove_vertex(G, x) \endcode removes the vertex x, if it is there;
  • -
  • \code add_edge(G, x, y) \endcode adds the edge from the vertices x to y, if it is not there;
  • -
  • \code remove_edge(G, x, y) \endcode removes the edge from the vertices x to y, if it is there;
  • -
  • \code get_vertex_value(G, x) \endcode returns the value associated with the vertex x;
  • -
  • \code set_vertex_value(G, x, v) \endcode sets the value associated with the vertex x to v.
  • -
-Structures that associate values to the edges usually also provide: -
    -
  • \code get_edge_value(G, x, y) \endcode returns the value associated with the edge (x, y);
  • -
  • \code set_edge_value(G, x, y, v) \endcode sets the value associated with the edge (x, y) to v.
  • -
- * - */ -namespace lzhlib -{ - namespace exceptions - { - class require_edge_that_does_not_exist; - } - class vertex_id - { - friend struct invalid_vertex; - friend class exceptions::require_edge_that_does_not_exist; //for vertex_id::id() - private: - vertex_id(std::size_t i) - : id_ {i} - {} - vertex_id(stock_id i) - : id_(i) - {} - public: - vertex_id() = default; - bool operator<(vertex_id rhs) const - { - return id_ < rhs.id_; - } - bool operator==(vertex_id rhs) const - { - return id_ == rhs.id_; - } - private: - auto id() const noexcept - { - return id_; - } - stock_id id_ {static_cast(-1)}; - }; - struct invalid_vertex - { - static constexpr vertex_id id {}; - }; - constexpr vertex_id invalid_vertex_id = invalid_vertex::id; - class edge_id - { - private: - edge_id() = default; - edge_id(std::size_t i) - : id_ {i} - {} - edge_id(stock_id i) - : id_(i) - {} - public: - bool operator<(edge_id rhs) const - { - return id_ < rhs.id_; - } - bool operator==(edge_id rhs) const - { - return id_ == rhs.id_; - } - bool operator!=(edge_id rhs) const - { - return !(*this == rhs); - } - private: - auto id() const noexcept - { - return id_; - } - stock_id id_; - }; - - namespace exceptions - { - class require_edge_that_does_not_exist: public std::logic_error - { - public: - require_edge_that_does_not_exist() - : std::logic_error(std::string("require edge that doesn't exist!")) - { - - } - }; - [[noreturn]] void throw_exception_require_edge_that_does_not_exist() - { - throw exceptions::require_edge_that_does_not_exist(); - } - } - - class null_value_tag; - namespace detail - { - - template - class vertex; - template <> - class vertex - { - - public: - using vertex_value_t = null_value_tag; - class edge_ref - { - public: - edge_id associated_edge() const - { - return edge_; - } - vertex_id opposite_vertex() const - { - return vertex_; - } - bool is_connected(vertex_id v) const - { - return vertex_ == v; - } - - bool operator<(edge_ref rhs) const - { - return edge_ < rhs.edge_; - } - bool operator==(edge_ref rhs) const - { - return edge_ == rhs.edge_; - } - bool operator!=(edge_ref rhs) const - { - return !(*this == rhs); - } - private: - edge_id edge_; - vertex_id vertex_; - }; -// bool is_associated(edge_id i) -// { -// return edges.find(i)!=edges.end(); //a possibly more efficent way :ask edge for this -// } - std::setconst& associated_edges() const - { - return edges; - } - void add_associated_edge(edge_ref i) - { - edges.insert(i); - } - void remove_associated_edge(edge_ref i) //i.asociated_edge()对应的边将会移除.忽略i.opposite_vertex的值. - { - edges.erase(i); - } - - bool adjacent(vertex_id v) const - { - std::set const& edges = associated_edges(); - for(edge_ref r : edges) - { - if(r.is_connected(v)) - return true; - } - return false; - } - bool associated(edge_id e) const - { - std::set const& edges = associated_edges(); - for(edge_ref r : edges) - { - if(r.associated_edge() == e) - return true; - } - return false; - } - edge_id associated_edge(vertex_id y) const - { - std::set const& edges = associated_edges(); - for(edge_ref e : edges) - { - if(e.is_connected(y)) - return e.associated_edge(); - } - exceptions::throw_exception_require_edge_that_does_not_exist(); - } - private: - std::set edges; - }; - template - class vertex: public vertex - { - public: - using vertex_value_t = VertexValueT; - template - vertex(Args&&... args) - : value(std::forward(args)...) - { - - } - vertex_value_t& vertex_value() & - { - return value; - } - vertex_value_t&& vertex_value() && - { - return std::move(value); - } - vertex_value_t const& vertex_value() const& - { - return value; - } - vertex_value_t const&& vertex_value() const&& - { - return std::move(value); - } - private: - vertex_value_t value; - }; - - template - class edge; - template <> - class edge - { - public: - using edge_value_t = null_value_tag; - using pair_t = std::pair; - bool is_associated(vertex_id i) const - { - return i == vertices.first || i == vertices.second; - } - pair_t get_associated_vertices() const - { - return vertices; - } - void set_associated_vertices(vertex_id v1, vertex_id v2) - { - vertices.first = v1; - vertices.second = v2; - } - void detach() - { - vertices.first = vertices.second = invalid_vertex_id; - } - private: - pair_t vertices; //对于 undirected graph,vertices.first 与vertices.second 的地位相同.对于directed graph,vertices.first为边的起点,vertices.second为边的终点. - }; - template - class edge: public edge - { - public: - using edge_value_t = EdgeValueT; - template - edge(Args&&...args) - : value(std::forward(args)...) - { - } - edge_value_t& edge_value() & - { - return value; - } - edge_value_t&& edge_value() && - { - return std::move(value); - } - edge_value_t const& edge_value() const& - { - return value; - } - edge_value_t const&& edge_value() const&& - { - return std::move(value); - } - private: - edge_value_t value; - }; - } - - - template - class undirected_graph - { - public: - using vertex_t = detail::vertex; - using vertex_value_t = typename vertex_t::vertex_value_t; - using edge_ref_t = typename vertex_t::edge_ref; - using edge_t = detail::edge; - using edge_value_t = typename edge_t::edge_value_t; - using pair_t = typename edge_t::pair_t; - - bool adjacent(vertex_id x, vertex_id y) const - { - if(get_vertex(x).associated_edges().size() < get_vertex(y).associated_edges().size()) - return get_vertex(x).adjacent(y); - else - return get_vertex(y).adjacent(x); - } - std::vector neighbors(vertex_id v) const //不如让用户 auto const& s = graph.associated_edges(v); - { - std::set const& edges = associated_edges(v); //在 for(auto e : s)中 - std::vector ret; //再 vertex_id v = e.opposite_vertex(); - ret.reserve(edges.size()); - for(edge_ref_t e : edges) - { - ret.push_back(e.opposite_vertex()); - } - return ret; - } - std::setconst& associated_edges(vertex_id v) const - { - return get_vertex(v).get_associated_edges(); - } - template - vertex_id add_vertex(Args&&...args) - { - return vertex_repository.add_stock(std::forward(args)...); - } - void remove_vertex(vertex_id v) - { - std::set const& edges = get_vertex(v).get_associated_edges(); - for(edge_ref_t e : edges) - { - get_vertex(e.opposite_vertex()).remove_associated_edge(e); - edge_repository.remove_stock(e.associated_edge()); - } - vertex_repository.remove_stock(v); - } - template - edge_id add_edge(vertex_id x, vertex_id y, Args&& ...args) - { - edge_id result = edge_repository.add_stock(std::forward(args)...); - get_edge(result).set_associated_vertices(x, y); - get_vertex(x).add_associated_edge( {result, y}); - get_vertex(y).add_associated_edge( {result, x}); - return result; - } - void remove_edge(vertex_id x, vertex_id y) - { - remove_edge(get_edge(x, y)); - } - void remove_edge(edge_id e) - { - pair_t vertices = get_edge(e).get_associated_vertices(); - get_vertex(vertices.first).remove_associated_edge( {e, vertices.second}); - get_vertex(vertices.second).remove_associated_edge( {e, vertices.first}); - edge_repository.remove_stock(e.id()); - } - - edge_id get_edge(vertex_id x, vertex_id y) const - { - assert(adjacent(x, y)); - if(get_vertex(x).associated_edges().size() < get_vertex(y).associated_edges().size()) - return get_vertex(x).associated_edge(y); - else - return get_vertex(y).associated_edge(x); - } - pair_t get_associated_vertices(edge_id e) const - { - return get_edge(e).get_associated_vertices(); - } - - vertex_value_t& value(vertex_id v) - { - return get_vertex(v).vertex_value(); - } - vertex_value_t const& value(vertex_id v) const - { - return get_vertex(v).vertex_value(); - } - edge_value_t& value(edge_id e) - { - return get_edge(e).edge_value(); - } - edge_value_t const& value(edge_id e) const - { - return get_edge(e).edge_value(); - } - - vertex_id first_vertex() const - { - return vertex_repository.first_stock(); - } - bool is_last_vertex(vertex_id id) const - { - return vertex_repository.is_last_stock(id); - } - vertex_id next_vertex(vertex_id id) const - { - return vertex_repository.next_stock(id); - } - private: - vertex_t& get_vertex(vertex_id v) - { - return vertex_repository.get_stock(v.id()); - } - vertex_t const& get_vertex(vertex_id v) const - { - return vertex_repository.get_stock(v.id()); - } - edge_t& get_edge(edge_id e) - { - return edge_repository.get_stock(e.id()); - } - edge_t const& get_edge(edge_id e) const - { - return edge_repository.get_stock(e.id()); - } - - repository vertex_repository; - repository edge_repository; - }; - - - - template - class directed_graph - { - public: - using vertex_t = detail::vertex; - using vertex_value_t = typename vertex_t::vertex_value_t; - using edge_ref_t = typename vertex_t::edge_ref; - using edge_t = detail::edge; - using edge_value_t = typename edge_t::edge_value_t; - using pair_t = typename edge_t::pair_t; - - - bool adjacent(vertex_id x, vertex_id y) const; - - protected: - vertex_t& get_vertex(vertex_id v) - { - return vertex_repository.get_stock(v.id()); - } - vertex_t const& get_vertex(vertex_id v) const - { - return vertex_repository.get_stock(v.id()); - } - edge_t& get_edge(edge_id e) - { - return edge_repository.get_stock(e.id()); - } - edge_t const& get_edge(edge_id e) const - { - return edge_repository.get_stock(e.id()); - } - repository vertex_repository; - repository edge_repository; - }; -} - - - -#endif // GRAPH_H_INCLUDED diff --git a/include/directed_graph.h b/include/directed_graph.h new file mode 100644 index 0000000..8fc3a35 --- /dev/null +++ b/include/directed_graph.h @@ -0,0 +1,64 @@ + +#ifndef GRAPH_DIRECTED_GRAPH_H_H +#define GRAPH_DIRECTED_GRAPH_H_H + +#include "Graph/include/graph_base.h" + +namespace lzhlib +{ + template + class directed_graph : public graph_base + { + public: + using base = graph_base; + using vertex_t = detail::vertex; + using vertex_value_t = typename vertex_t::vertex_value_t; + using edge_ref_t = typename detail::edge_ref; + using edge_t = detail::edge; + using edge_value_t = typename edge_t::edge_value_t; + using pair_t = typename edge_t::pair_t; + + + bool adjacent(vertex_id x, vertex_id y) const + { + return base::get_vertex(x).adjacent(y); + } + + void remove_vertex(vertex_id v) + { + auto const& edges = base::get_vertex(v).associated_edges(); + for (edge_ref_t e : edges) + { + base::edge_repository.remove_stock(base::to_stock_id(e.edge())); + } + base::vertex_repository.remove_stock(base::to_stock_id(v)); + } + + template + edge_id add_edge(vertex_id x, vertex_id y, Args&& ...args) + { + edge_id result = base::to_edge_id(base::edge_repository + .add_stock(std::forward(args)...)); + base::get_edge(result).set_associated_vertices(x, y); + base::get_vertex(x).add_associated_edge({result, y}); + return result; + } + void remove_edge(vertex_id x, vertex_id y) + { + remove_edge(get_edge(x, y)); + } + void remove_edge(edge_id e) + { + pair_t vertices = base::get_edge(e).associated_vertices(); + base::get_vertex(vertices.first).remove_associated_edge(e); + base::edge_repository.remove_stock(base::to_stock_id(e)); + } + + edge_id get_edge(vertex_id x, vertex_id y) const + { + assert(adjacent(x, y)); + return base::get_vertex(x).associated_edge(y); + } + }; +} +#endif //GRAPH_DIRECTED_GRAPH_H_H diff --git a/include/edge.h b/include/edge.h new file mode 100644 index 0000000..b0b4aee --- /dev/null +++ b/include/edge.h @@ -0,0 +1,81 @@ + +#ifndef GRAPH_EDGE_H +#define GRAPH_EDGE_H + +#include //for std::pair +#include "Graph/include/vertex_id.h" + +namespace lzhlib +{ + class null_value_tag; + namespace detail + { + template + class edge; + + template<> + class edge + { + public: + using edge_value_t = null_value_tag; + using pair_t = std::pair; + + bool is_associated(vertex_id i) const + { + return i == vertices.first || i == vertices.second; + } + pair_t associated_vertices() const + { + return vertices; + } + void set_associated_vertices(vertex_id v1, vertex_id v2) + { + vertices.first = v1; + vertices.second = v2; + } + void detach() + { + vertices.first = vertices.second = invalid_vertex_id; + } + + private: + pair_t vertices; //对于 undirected graph,vertices.first 与vertices.second 的地位相同.对于directed graph,vertices.first为边的起点,vertices.second为边的终点. + }; + + template + class edge : public edge + { + public: + using edge_value_t = EdgeValueT; + + template + edge(Args&& ...args) + : value(std::forward(args)...) + { + } + + edge_value_t& edge_value()& + { + return value; + } + edge_value_t&& edge_value()&& + { + return std::move(value); + } + edge_value_t const& edge_value() const& + { + return value; + } + edge_value_t const&& edge_value() const&& + { + return std::move(value); + } + + private: + edge_value_t value; + }; + } +} + + +#endif //GRAPH_EDGE_H diff --git a/include/edge_id.h b/include/edge_id.h new file mode 100644 index 0000000..9962e45 --- /dev/null +++ b/include/edge_id.h @@ -0,0 +1,45 @@ +#ifndef GRAPH_EDGE_ID_H +#define GRAPH_EDGE_ID_H + +#include //for std::size_t +#include "Graph/include/stock_id.h" + +namespace lzhlib +{ + class edge_id + { + template + friend + class graph_base; + + private: + edge_id() = default; + edge_id(std::size_t i) + : id_{i} + {} + edge_id(stock_id i) + : id_(i) + {} + public: + bool operator<(edge_id rhs) const + { + return id_ < rhs.id_; + } + bool operator==(edge_id rhs) const + { + return id_ == rhs.id_; + } + bool operator!=(edge_id rhs) const + { + return !(*this == rhs); + } + private: + auto id() const noexcept + { + return id_; + } + stock_id id_; + }; + +} +#endif //GRAPH_EDGE_ID_H diff --git a/include/graph.h b/include/graph.h new file mode 100644 index 0000000..1598c4b --- /dev/null +++ b/include/graph.h @@ -0,0 +1,29 @@ +#ifndef GRAPH_H_INCLUDED +#define GRAPH_H_INCLUDED + +#include "Graph/include/undirected_graph.h" +#include "Graph/include/directed_graph.h" +/** \file + * \brief 本文件提供与 [图](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) 有关的抽象. + * + * ##图 支持的操作:(摘自维基百科) +
    +
  • \code adjacent(G, x, y) \endcode tests whether there is an edge from the vertices x to y;
  • +
  • \code neighbors (G, x) \endcode lists all vertices y such that there is an edge from the vertices x to y;
  • +
  • \code add_vertex(G, x) \endcode adds the vertex x, if it is not there;
  • +
  • \code remove_vertex(G, x) \endcode removes the vertex x, if it is there;
  • +
  • \code add_edge(G, x, y) \endcode adds the edge from the vertices x to y, if it is not there;
  • +
  • \code remove_edge(G, x, y) \endcode removes the edge from the vertices x to y, if it is there;
  • +
  • \code get_vertex_value(G, x) \endcode returns the value associated with the vertex x;
  • +
  • \code set_vertex_value(G, x, v) \endcode sets the value associated with the vertex x to v.
  • +
+Structures that associate values to the edges usually also provide: +
    +
  • \code get_edge_value(G, x, y) \endcode returns the value associated with the edge (x, y);
  • +
  • \code set_edge_value(G, x, y, v) \endcode sets the value associated with the edge (x, y) to v.
  • +
+ * + */ + + +#endif // GRAPH_H_INCLUDED diff --git a/include/graph_base.h b/include/graph_base.h new file mode 100644 index 0000000..993b388 --- /dev/null +++ b/include/graph_base.h @@ -0,0 +1,121 @@ +#ifndef GRAPH_GRAPH_BASE_H +#define GRAPH_GRAPH_BASE_H + +#include "Graph/include/vertex.h" +#include "Graph/include/edge.h" +#include "Graph/include/repository.h" + +namespace lzhlib +{ + template + class graph_base + { + public: + using vertex_t = detail::vertex; + using vertex_value_t = typename vertex_t::vertex_value_t; + using edge_ref_t = typename detail::edge_ref; + using edge_t = detail::edge; + using edge_value_t = typename edge_t::edge_value_t; + using pair_t = typename edge_t::pair_t; + + bool associated(vertex_id v, edge_id e) + { + return get_vertex(v).associated(e); + } + std::vector neighbors(vertex_id v) const //不如让用户 auto const& s = graph.associated_edges(v); + { + auto const& edges = associated_edges(v); //在 for(auto e : s)中 + std::vector ret; //再 vertex_id v = e.opposite_vertex(); + ret.reserve(edges.size()); + for (edge_ref_t e : edges) + ret.push_back(e.opposite_vertex()); + return ret; + } + auto const& associated_edges(vertex_id v) const //比neighbors更建议使用(出于效率考虑) + { + return get_vertex(v).associated_edges(); + } + + template + vertex_id add_vertex(Args&& ...args) + { + return vertex_repository.add_stock(std::forward(args)...); + } + + pair_t associated_vertices(edge_id e) const + { + return get_edge(e).associated_vertices(); + } + + vertex_value_t& value(vertex_id v) + { + return get_vertex(v).vertex_value(); + } + vertex_value_t const& value(vertex_id v) const + { + return get_vertex(v).vertex_value(); + } + edge_value_t& value(edge_id e) + { + return get_edge(e).edge_value(); + } + edge_value_t const& value(edge_id e) const + { + return get_edge(e).edge_value(); + } + + vertex_id first_vertex() const + { + return vertex_id{vertex_repository.first_stock()}; + } + + bool vertex_end(vertex_id id) const + { + return vertex_repository.stock_end(id.id()); + } + + vertex_id next_vertex(vertex_id id) const + { + return vertex_repository.next_stock(id.id()); + } + + protected: + + vertex_t& get_vertex(vertex_id v) + { + return vertex_repository.get_stock(v.id()); + } + vertex_t const& get_vertex(vertex_id v) const + { + return vertex_repository.get_stock(v.id()); + } + edge_t& get_edge(edge_id e) + { + return edge_repository.get_stock(e.id()); + } + edge_t const& get_edge(edge_id e) const + { + return edge_repository.get_stock(e.id()); + } + + vertex_id to_vertex_id(stock_id i) //preserved.Direved class don't need this just for now. + { + return i; + } + edge_id to_edge_id(stock_id i) + { + return i; + } + stock_id to_stock_id(vertex_id v) + { + return v.id(); + } + stock_id to_stock_id(edge_id e) + { + return e.id(); + } + repository vertex_repository; + repository edge_repository; + }; +} +#endif //GRAPH_GRAPH_BASE_H diff --git a/include/repository.h b/include/repository.h index 5588038..57b4088 100644 --- a/include/repository.h +++ b/include/repository.h @@ -1,32 +1,15 @@ #ifndef REPOSITORY_H_INCLUDED #define REPOSITORY_H_INCLUDED -#include //for std::size_t -#include -#include -#include -#include -#include + +#include //for std::out_of_range +#include //for std::vector +#include //for std::unique_ptr +#include //for std::set +#include "Graph/include/stock_id.h" + namespace lzhlib { - class stock_id - { - public: - bool operator<(stock_id rhs) const - { - return id_ < rhs.id_; - } - bool operator==(stock_id rhs) const - { - return id_ == rhs.id_; - } - auto id() const noexcept - { - return id_; - } - private: - std::size_t id_; - }; - template + template class repository { public: @@ -34,29 +17,34 @@ namespace lzhlib using id_t = stock_id; using pointer_t = std::unique_ptr; - class attempt_to_use_unassigned_stock: public std::out_of_range + class attempt_to_use_unassigned_stock : public std::out_of_range { public: attempt_to_use_unassigned_stock(id_t id) - : out_of_range(std::string("Attempt to use unassigned stock which id is ") + std::to_string(id.id()) + "!") + : out_of_range(std::string("Attempt to use unassigned stock which id is ") + + std::to_string(id.id()) + "!") { } }; - class attempt_to_remove_nonexistent_stock: public std::out_of_range + + class attempt_to_remove_nonexistent_stock : public std::out_of_range { public: attempt_to_remove_nonexistent_stock(id_t id) - : out_of_range(std::string("Attempt to use unremovable stock which id is ") + std::to_string(id.id()) + "!") + : out_of_range(std::string("Attempt to use unremovable stock which id is ") + + std::to_string(id.id()) + "!") { } }; - class attempt_to_reuse_unreusable_stock: public std::out_of_range + + class attempt_to_reuse_unreusable_stock : public std::out_of_range { public: attempt_to_reuse_unreusable_stock(id_t id) - : out_of_range(std::string("Attempt to reuse unreusable stock which id is ") + std::to_string(id.id()) + "!") + : out_of_range(std::string("Attempt to reuse unreusable stock which id is ") + + std::to_string(id.id()) + "!") { } }; @@ -64,7 +52,7 @@ namespace lzhlib stock_t& get_stock(id_t id) { #ifndef NDEBUG - if(is_not_found_in_living_stocks(id)) + if (is_not_valid(id)) { throw attempt_to_use_unassigned_stock(id); } @@ -74,100 +62,95 @@ namespace lzhlib stock_t const& get_stock(id_t id) const { #ifndef NDEBUG - if(is_not_found_in_living_stocks(id)) + if (is_not_valid(id)) { throw attempt_to_use_unassigned_stock(id); } #endif // NDEBUG return *stocks[id.id()]; } - template - id_t add_stock(Args&&... args) + template + id_t add_stock(Args&& ... args) { - if(no_reusable_stocks()) + id_t ret = reusable_stock(); + if (ret == stocks.size()) //no reusable stock { return allocate_stock(std::forward(args)...); } else { - return reuse_stock(std::forward(args)...); + return reuse_stock(ret,std::forward(args)...); } } void remove_stock(id_t id) { #ifndef NDEBUG - if(is_not_found_in_living_stocks(id)) + if (is_not_valid(id)) throw attempt_to_remove_nonexistent_stock(id); #endif // NDEBUG stocks[id.id()].reset(); - ids_of_living_stocks.erase(id); - ids_of_reusable_stocks.insert(id); } id_t first_stock() const { - return *ids_of_living_stocks.begin(); + return stock_at_or_after(id_t{0}); } - bool is_last_stock(id_t current_stock) const + bool stock_end(id_t current) const { - return ++(ids_of_living_stocks.find(current_stock)) == ids_of_living_stocks.end(); + return current.id() == stocks.size(); } - id_t next_stock(id_t current_stock) const + id_t next_stock(id_t current) const //precondition: current.id() < stocks.size() { - return *++(ids_of_living_stocks.find(current_stock)); - } + return stock_at_or_after(++current); //postcondition: 设返回值为next,则stocks[next.id()]为current代表的位置或current代表的位置之后的位置上的有效stock的左值, + } // 或者next.id() == stocks.size()(current代表的位置之后的位置上均无有效stock + private: #ifndef NDEBUG - bool is_not_found_in_living_stocks(id_t id) const + bool is_not_valid(id_t id) const { - return ids_of_living_stocks.find(id) == ids_of_living_stocks.end(); + return stocks[id.id()] == nullptr; } #endif // NDEBUG bool no_reusable_stocks() { - return ids_of_reusable_stocks.empty(); + } - template - id_t allocate_stock(Args&&... args) + template + id_t allocate_stock(Args&& ... args) { stocks.push_back(std::make_unique(std::forward(args)...)); - id_t ret {stocks.size() - 1}; //the allocated stock is at the last position in the container. - ids_of_living_stocks.insert(ret); - return ret; - } - template - id_t reuse_stock(Args&&... args) - { - typename std::set::iterator i = iterator_of_a_reusable_stock(); - id_t ret = *i; - reusable_pointer(ret) = std::make_unique(std::forward(args)...); - ids_of_reusable_stocks.erase(i); - ids_of_living_stocks.insert(ret); + id_t ret{stocks.size() - + 1}; //the allocated stock is at the last position in the container. return ret; } - typename std::set::iterator iterator_of_a_reusable_stock() const + template + id_t reuse_stock(id_t reused, Args&& ... args)//precondition:stocks[reused.id()]必须为一个空指针 { - return ids_of_reusable_stocks.begin(); + reusable_pointer(reused) = std::make_unique(std::forward(args)...); + return reused; + } + id_t reusable_stock() const //postcondition: 设返回值为ret,则stocks[ret.id()]为一个空指针. + { //或者ret.id() == stocks.size()(current代表的位置及current代表的位置之后的位置上均无reusable stock + id_t current{0}; + while (current.id() != stocks.size() && stocks[current.id()] != nullptr) + ++current; + return current; } pointer_t& reusable_pointer(id_t id) //just a checker.The calling may be optimized out -- that's to say, may be inlined. { #ifndef NDEBUG - if(is_not_found_in_reusable_stocks(id)) - { + if(stocks[id.id()] != nullptr) throw attempt_to_reuse_unreusable_stock(id); - } #endif // NDEBUG return stocks[id.id()]; } -#ifndef NDEBUG - bool is_not_found_in_reusable_stocks(id_t id) const + id_t stock_at_or_after(id_t current) const //precondition: current.id() <= stocks.size() { - return ids_of_reusable_stocks.find(id) == ids_of_reusable_stocks.end(); - } -#endif // NDEBUG + while (current.id() != stocks.size() && stocks[current.id()] == nullptr) + ++current; + return current; //postcondition: 设返回值为next,则stocks[next.id()]为指向current代表的位置或current代表的位置之后的位置上的有效stock的指针的左值, + } //或者next.id() == stocks.size()(current代表的位置及current代表的位置之后的位置上均无有效stock private: std::vector stocks; - std::set ids_of_living_stocks; - std::set ids_of_reusable_stocks; }; } diff --git a/include/stock_id.h b/include/stock_id.h new file mode 100644 index 0000000..bab8eb9 --- /dev/null +++ b/include/stock_id.h @@ -0,0 +1,39 @@ +#ifndef GRAPH_STOCK_ID_H +#define GRAPH_STOCK_ID_H + +#include //for std::size_t + +namespace lzhlib +{ + class stock_id + { + public: + constexpr stock_id(std::size_t i) + : id_(i) + {} + stock_id() = default; + bool operator<(stock_id rhs) const + { + return id_ < rhs.id_; + } + + bool operator==(stock_id rhs) const + { + return id_ == rhs.id_; + } + + auto id() const noexcept + { + return id_; + } + stock_id& operator++() + { + ++id_; + return *this; + } + + private: + std::size_t id_; + }; +} +#endif //GRAPH_STOCK_ID_H diff --git a/include/undirected_graph.h b/include/undirected_graph.h new file mode 100644 index 0000000..30f7c8c --- /dev/null +++ b/include/undirected_graph.h @@ -0,0 +1,74 @@ + +#ifndef GRAPH_UNDIRECTED_GRAPH_H +#define GRAPH_UNDIRECTED_GRAPH_H + +#include "Graph/include/graph_base.h" + +namespace lzhlib +{ + + template + class undirected_graph : public graph_base + { + public: + using base = graph_base; + using vertex_t = detail::vertex; + using vertex_value_t = typename vertex_t::vertex_value_t; + using edge_ref_t = typename detail::edge_ref; + using edge_t = detail::edge; + using edge_value_t = typename edge_t::edge_value_t; + using pair_t = typename edge_t::pair_t; + + bool adjacent(vertex_id x, vertex_id y) const + { + if (base::get_vertex(x).associated_edges().size() < + base::get_vertex(y).associated_edges().size()) + return base::get_vertex(x).adjacent(y); + else + return base::get_vertex(y).adjacent(x); + } + + void remove_vertex(vertex_id v) + { + auto const& edges = base::get_vertex(v).associated_edges(); + for (edge_ref_t e : edges) + { + base::get_vertex(e.opposite_vertex()).remove_associated_edge(e.edge()); + base::edge_repository.remove_stock(base::to_stock_id(e.edge())); + } + base::vertex_repository.remove_stock(base::to_stock_id(v)); + } + + template + edge_id add_edge(vertex_id x, vertex_id y, Args&& ...args) + { + edge_id result = base::to_edge_id(base::edge_repository.add_stock(std::forward(args)...)); + base::get_edge(result).set_associated_vertices(x, y); + base::get_vertex(x).add_associated_edge({result, y}); + base::get_vertex(y).add_associated_edge({result, x}); + return result; + } + void remove_edge(vertex_id x, vertex_id y) + { + remove_edge(get_edge(x, y)); + } + void remove_edge(edge_id e) + { + pair_t vertices = base::get_edge(e).associated_vertices(); + base::get_vertex(vertices.first).remove_associated_edge(e); + base::get_vertex(vertices.second).remove_associated_edge(e); + base::edge_repository.remove_stock(base::to_stock_id(e)); + } + + edge_id get_edge(vertex_id x, vertex_id y) const + { + assert(adjacent(x, y)); + if (base::get_vertex(x).associated_edges().size() < + base::get_vertex(y).associated_edges().size()) + return base::get_vertex(x).associated_edge(y); + else + return base::get_vertex(y).associated_edge(x); + } + }; +} +#endif //GRAPH_UNDIRECTED_GRAPH_H diff --git a/include/vertex.h b/include/vertex.h new file mode 100644 index 0000000..c177cb8 --- /dev/null +++ b/include/vertex.h @@ -0,0 +1,177 @@ + +#ifndef GRAPH_VERTEX_H +#define GRAPH_VERTEX_H + +#include //for std::less +#include //for std::set +#include //for std::logic_error +#include "Graph/include/edge_id.h" +#include "Graph/include/vertex_id.h" + +namespace lzhlib +{ + namespace exceptions + { + class require_edge_that_does_not_exist : public std::logic_error + { + public: + require_edge_that_does_not_exist() + : std::logic_error(std::string("require edge that doesn't exist!")) + { + + } + }; + } + + class null_value_tag; + namespace detail + { + class edge_ref + { + public: + edge_ref(edge_id e, vertex_id opposite) + : edge_(e), vertex_(opposite) + {} + edge_id edge() const + { + return edge_; + } + vertex_id opposite_vertex() const + { + return vertex_; + } + bool is_connected(vertex_id v) const + { + return vertex_ == v; + } + + bool operator<(edge_ref rhs) const //默认忽略opposite_vertex() + { + return edge_ < rhs.edge_; + } + bool operator==(edge_ref rhs) const + { + return edge_ == rhs.edge_; + } + bool operator!=(edge_ref rhs) const + { + return !(*this == rhs); + } + + private: + edge_id edge_; + vertex_id vertex_; + }; + + inline bool operator<(edge_ref r, edge_id rhs) + { + return r.edge() < rhs; + } + inline bool operator<(edge_id rhs, edge_ref r) + { + return rhs < r.edge(); + } + inline bool operator<(edge_ref r, vertex_id rhs) //仍可依据opposite_vertex()来比较 + { + return r.opposite_vertex() < rhs; + } + inline bool operator<(vertex_id rhs, edge_ref r) //仍可依据opposite_vertex()来比较 + { + return rhs < r.opposite_vertex(); + } + + + template + class vertex; + + template<> + class vertex + { + + public: + using vertex_value_t = null_value_tag; + +// bool is_associated(edge_id i) +// { +// return edges.find(i)!=edges.end(); //a possibly more efficent way :ask edge for this +// } + auto const& associated_edges() const + { + return edges; + } + void add_associated_edge(edge_ref i) + { + edges.insert(i); + } + void remove_associated_edge(edge_id i) + { + auto to_be_erased = edges.find(i); + edges.erase(to_be_erased); + } + + bool adjacent(vertex_id v) const + { + auto const& edges = associated_edges(); + if (edges.find(v) != edges.end()) + return true; + return false; + } + bool associated(edge_id e) const + { + auto const& edges = associated_edges(); + if (edges.find(e) != edges.end()) + return true; + return false; + } + + edge_id associated_edge(vertex_id y) const + { + auto const& edges = associated_edges(); + for (edge_ref e : edges) + { + if (e.is_connected(y)) + return e.edge(); + } + throw exceptions::require_edge_that_does_not_exist(); + } + + private: + std::set> edges; //for std::less::is_transparent --> for comparision between differnet types + }; + + template + class vertex : public vertex + { + public: + using vertex_value_t = VertexValueT; + + template + vertex(Args&& ... args) + : value(std::forward(args)...) + { + + } + + vertex_value_t& vertex_value()& + { + return value; + } + vertex_value_t&& vertex_value()&& + { + return std::move(value); + } + vertex_value_t const& vertex_value() const& + { + return value; + } + vertex_value_t const&& vertex_value() const&& + { + return std::move(value); + } + + private: + vertex_value_t value; + }; + } +} +#endif //GRAPH_VERTEX_H diff --git a/include/vertex_id.h b/include/vertex_id.h new file mode 100644 index 0000000..e90768b --- /dev/null +++ b/include/vertex_id.h @@ -0,0 +1,50 @@ +#ifndef GRAPH_VERTEX_ID_H +#define GRAPH_VERTEX_ID_H + +#include //for std::size_t +#include "Graph/include/stock_id.h" + +namespace lzhlib +{ + + class vertex_id + { + friend struct invalid_vertex; + + template + friend + class graph_base; + + private: + vertex_id(std::size_t i) + : id_{i} + {} + vertex_id(stock_id i) + : id_(i) + {} + public: + vertex_id() = default; + bool operator<(vertex_id rhs) const + { + return id_ < rhs.id_; + } + bool operator==(vertex_id rhs) const + { + return id_ == rhs.id_; + } + private: + auto id() const noexcept + { + return id_; + } + stock_id id_{static_cast(-1)}; + }; + + struct invalid_vertex + { + static constexpr vertex_id id{}; + }; + constexpr vertex_id invalid_vertex_id = invalid_vertex::id; + +} +#endif //GRAPH_VERTEX_ID_H diff --git a/test/test.cpp b/test/test.cpp index 08bbbd2..9845ba3 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,166 +1,179 @@ #include #include -#define private public -#include "Graph/include/Graph.h" + +#include "Graph/include/graph.h" +#include "Graph/test/white_box_test.h" + using namespace std; using namespace lzhlib; using namespace lzhlib::detail; -void test_vertex() -{ - vertex v {}; - vertex::edge_ref e1 {1,vertex_id()}, e2 {2,vertex_id()}; - v.add_associated_edge(e1); - auto s = v.associated_edges(); - assert(s.size() == 1); - assert(s.find(e1) != s.end() && s.find(e2) == s.end()); - - v.add_associated_edge(e2); - s = v.associated_edges(); - assert(s.size() == 2); - assert(s.find(e1) != s.end() && s.find(e2) != s.end()); - - v.remove_associated_edge(e1); - s = v.associated_edges(); - assert(s.size() == 1); - assert(s.find(e1) == s.end() && s.find(e2) != s.end()); - - v.remove_associated_edge(e2); - s = v.associated_edges(); - assert(s.empty()); - assert(s.find(e1) == s.end() && s.find(e2) == s.end()); - - v.vertex_value() = 1; - assert(v.vertex_value() == 1); - v.vertex_value() = 2; - assert(v.vertex_value() == 2); -} -void test_edge() -{ - edge e; - vertex_id v1 {0}, v2 {1}; - e.set_associated_vertices(v1, v2); - assert(e.is_associated(v1) && e.is_associated(v2)); - - edge::pair_t p1 = e.get_associated_vertices(); - assert((p1.first == v1 && p1.second == v2) || (p1.first == v2 && p1.first == v1)); - - e.detach(); - p1 = e.get_associated_vertices(); - assert(p1.first == invalid_vertex_id && p1.second == invalid_vertex_id); - assert(!e.is_associated(v1) && !e.is_associated(v2)); - - e.edge_value() = 1; - assert(e.edge_value() == 1); - e.edge_value() = 2; - assert(e.edge_value() == 2); -} -void test_vertex_repository() -{ - repository> r0; - auto i0 = r0.add_stock(1); - assert(r0.get_stock(i0).vertex_value() == 1); - - r0.remove_stock(i0); - try - { - r0.get_stock(i0); - } - catch(repository>::attempt_to_use_unassigned_stock e) - { - assert(e.what() == std::string("Attempt to use unassigned stock which id is ") + std::to_string(i0.id()) + "!"); - } - - auto i1 = r0.add_stock(2); - assert(r0.ids_of_living_stocks.size() == 1); - assert(r0.ids_of_reusable_stocks.size() == 0); - assert(r0.get_stock(i1).vertex_value() == 2); - - auto i2 = r0.add_stock(3); - assert(r0.ids_of_living_stocks.size() == 2); - assert(r0.ids_of_reusable_stocks.size() == 0); - assert(r0.get_stock(i1).vertex_value() == 2); - assert(r0.get_stock(i2).vertex_value() == 3); - - r0.remove_stock(i1); - assert(r0.ids_of_living_stocks.size() == 1); - assert(r0.ids_of_reusable_stocks.size() == 1); - assert(r0.get_stock(i2).vertex_value() == 3); - - r0.remove_stock(i2); - assert(r0.ids_of_living_stocks.size() == 0); - assert(r0.ids_of_reusable_stocks.size() == 2); -} -void test_vertex_edge_and_repository() -{ - repository> rv; - repository> re; - - auto v0 = rv.add_stock("a"); - auto v1 = rv.add_stock("b"); - auto e0 = re.add_stock("a and b"); - - rv.get_stock(v0).add_associated_edge( {e0,v1}); - rv.get_stock(v1).add_associated_edge( {e0,v0}); - re.get_stock(e0).set_associated_vertices(v0, v1); - - assert(re.get_stock(e0).is_associated(v0)); - assert(re.get_stock(e0).is_associated(v1)); - - auto const& s0 = rv.get_stock(v0).associated_edges(); - assert(s0.find( {e0,v1}) != s0.end()); - - auto const& s1 = rv.get_stock(v1).associated_edges(); - assert(s1.find( {e0,v0}) != s1.end()); -} class test_undirected_graph { public: void operator()() { + undirected_graph g; + vertex_id v0 = g.add_vertex("v0"); + vertex_id v1 = g.add_vertex("v1"); { - undirected_graph g; - vertex_id v0 = g.add_vertex("v0"); - vertex_id v1 = g.add_vertex("v1"); + edge_id e0 = g.add_edge(v0, v1); + assert(g.value(e0) == ""); + assert(g.get_edge(v0, v1) == e0); + g.value(e0) = "e0"; + assert(g.associated(v0, e0)); + assert(g.associated(v1, e0)); + assert(g.adjacent(v0, v1)); + assert(g.adjacent(v1, v0)); + assert(g.get_edge(v0, v1) == e0); + assert(g.get_edge(v1, v0) == e0); + assert(g.value(v0) == "v0"); + assert(const_cast(g).value(v0) == "v0"); + assert(g.value(v1) == "v1"); + assert(const_cast(g).value(v1) == "v1"); + assert(g.value(e0) == "e0"); + assert(const_cast(g).value(e0) == "e0"); { - edge_id e0 = g.add_edge(v0, v1); - assert(g.value(e0) == ""); - g.value(e0) = "e0"; - assert(g.adjacent(v0, v1)); - assert(g.adjacent(v1, v0)); - assert(g.get_edge(v0, v1) == e0); - assert(g.get_edge(v1, v0) == e0); - assert(g.value(v0) == "v0"); - assert(g.value(v1) == "v1"); - assert(g.value(e0) == "e0"); - - g.remove_edge(e0); - assert(!g.adjacent(v0, v1)); - assert(!g.adjacent(v1, v0)); + auto vertex = g.neighbors(v0); + assert(vertex.size() == 1); + assert(vertex[0] == v1); } { - edge_id e1 = g.add_edge(v0, v1, "e1"); - assert(g.value(e1) == "e1"); - assert(g.adjacent(v0, v1)); - assert(g.adjacent(v1, v0)); - assert(g.get_edge(v0, v1) == e1); - assert(g.get_edge(v1, v0) == e1); - - g.remove_edge(e1); - assert(!g.adjacent(v0, v1)); - assert(!g.adjacent(v1, v0)); + auto vertex = g.neighbors(v1); + assert(vertex.size() == 1); + assert(vertex[0] == v0); + } + { + auto edge_refs = g.associated_edges(v0); + assert(edge_refs.size() == 1); + assert((*edge_refs.begin()).edge() == e0); + assert((*edge_refs.begin()).opposite_vertex() == v1); } + { + auto edge_refs = g.associated_edges(v1); + assert(edge_refs.size() == 1); + assert((*edge_refs.begin()).edge() == e0); + assert((*edge_refs.begin()).opposite_vertex() == v0); + } + auto vertices = g.associated_vertices(e0); + assert(vertices.first == v0 && vertices.second == v1); + vector vertices0 = {vertices.first, vertices.second}; + vector vertices1; + for (auto v = g.first_vertex(); !g.vertex_end(v); v = g.next_vertex(v)) + vertices1.push_back(v); + assert(vertices0 == vertices1); + + g.remove_edge(e0); + assert(!g.associated(v0, e0)); + assert(!g.associated(v1, e0)); + assert(!g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); + } + { + edge_id e1 = g.add_edge(v0, v1, "e1"); + assert(g.value(e1) == "e1"); + assert(g.associated(v0, e1)); + assert(g.associated(v1, e1)); + assert(g.adjacent(v0, v1)); + assert(g.adjacent(v1, v0)); + assert(g.get_edge(v0, v1) == e1); + assert(g.get_edge(v1, v0) == e1); + + g.remove_edge(v0, v1); + assert(!g.associated(v0, e1)); + assert(!g.associated(v1, e1)); + assert(!g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); } + g.remove_vertex(v0); + g.remove_vertex(v1); } + private: }; + +void test_directed_graph() +{ + directed_graph g; + vertex_id v0 = g.add_vertex("v0"); + vertex_id v1 = g.add_vertex("v1"); + { + edge_id e0 = g.add_edge(v0, v1); + assert(g.value(e0) == ""); + assert(g.get_edge(v0, v1) == e0); + assert(g.associated(v0, e0)); + assert(!g.associated(v1, e0)); + assert(g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); + assert(g.get_edge(v0, v1) == e0); + //assert(g.get_edge(v1, v0) != e0); + assert(g.value(v0) == "v0"); + assert(const_cast(g).value(v0) == "v0"); + assert(g.value(v1) == "v1"); + assert(const_cast(g).value(v1) == "v1"); + g.value(e0) = "e0"; + assert(g.value(e0) == "e0"); + assert(const_cast(g).value(e0) == "e0"); + { + auto vertices = g.neighbors(v0); + assert(vertices.size() == 1); + assert(vertices[0] == v1); + } + { + auto vertices = g.neighbors(v1); + assert(vertices.empty()); + } + + { + auto edge_refs = g.associated_edges(v0); + assert(edge_refs.size() == 1); + assert((*edge_refs.begin()).edge() == e0); + assert((*edge_refs.begin()).opposite_vertex() == v1); + } + { + auto edge_refs = g.associated_edges(v1); + assert(edge_refs.empty()); + } + + { + auto vertices = g.associated_vertices(e0); + assert(vertices.first == v0 && vertices.second == v1); + vector vertices0 = {vertices.first, vertices.second}; + vector vertices1; + for (auto v = g.first_vertex(); !g.vertex_end(v); v = g.next_vertex(v)) + vertices1.push_back(v); + assert(vertices0 == vertices1); + } + + g.remove_edge(e0); + assert(!g.associated(v0, e0)); + assert(!g.associated(v1, e0)); + assert(!g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); + { + edge_id e1 = g.add_edge(v0, v1, "e1"); + assert(g.value(e1) == "e1"); + assert(g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); + assert(g.get_edge(v0, v1) == e1); + //assert(g.get_edge(v1, v0) != e1); + + g.remove_edge(v0, v1); + assert(!g.associated(v0, e0)); + assert(!g.associated(v1, e0)); + assert(!g.adjacent(v0, v1)); + assert(!g.adjacent(v1, v0)); + } + } + g.remove_vertex(v0); + g.remove_vertex(v1); +} + int main() { - test_vertex(); - test_edge(); - test_vertex_repository(); - test_vertex_edge_and_repository(); + white_box_test().test(); test_undirected_graph()(); + test_directed_graph(); cout << "Success!成功" << endl; return 0; } diff --git a/test/white_box_test.cpp b/test/white_box_test.cpp new file mode 100644 index 0000000..1adea1f --- /dev/null +++ b/test/white_box_test.cpp @@ -0,0 +1,111 @@ +#include + +#include "Graph/test/white_box_test.h" + +#define private public + +#include "Graph/include/graph.h" + +using namespace std; +using namespace lzhlib; +using namespace lzhlib::detail; + +void test_vertex() +{ + vertex v{}; + edge_ref e1{1, vertex_id()}, e2{2, vertex_id()}; + v.add_associated_edge(e1); + auto s = v.associated_edges(); + assert(s.size() == 1); + assert(s.find(e1) != s.end() && s.find(e2) == s.end()); + + v.add_associated_edge(e2); + s = v.associated_edges(); + assert(s.size() == 2); + assert(s.find(e1) != s.end() && s.find(e2) != s.end()); + + v.remove_associated_edge(e1.edge()); + s = v.associated_edges(); + assert(s.size() == 1); + assert(s.find(e1) == s.end() && s.find(e2) != s.end()); + + v.remove_associated_edge(e2.edge()); + s = v.associated_edges(); + assert(s.empty()); + assert(s.find(e1) == s.end() && s.find(e2) == s.end()); + + v.vertex_value() = 1; + assert(v.vertex_value() == 1); + v.vertex_value() = 2; + assert(v.vertex_value() == 2); +} + +void test_edge() +{ + edge e; + vertex_id v1{0}, v2{1}; + e.set_associated_vertices(v1, v2); + assert(e.is_associated(v1) && e.is_associated(v2)); + + edge::pair_t p1 = e.associated_vertices(); + assert((p1.first == v1 && p1.second == v2) || (p1.first == v2 && p1.first == v1)); + + e.detach(); + p1 = e.associated_vertices(); + assert(p1.first == invalid_vertex_id && p1.second == invalid_vertex_id); + assert(!e.is_associated(v1) && !e.is_associated(v2)); + + e.edge_value() = 1; + assert(e.edge_value() == 1); + e.edge_value() = 2; + assert(e.edge_value() == 2); +} + +void test_vertex_repository() +{ + repository> r0; + auto i0 = r0.add_stock(1); + assert(r0.get_stock(i0).vertex_value() == 1); + + r0.remove_stock(i0); + try + { + r0.get_stock(i0); + } + catch (repository>::attempt_to_use_unassigned_stock e) + { + assert(e.what() == std::string("Attempt to use unassigned stock which id is ") + + std::to_string(i0.id()) + "!"); + } + +} + +void test_vertex_edge_and_repository() +{ + repository> rv; + repository> re; + + auto v0 = rv.add_stock("a"); + auto v1 = rv.add_stock("b"); + auto e0 = re.add_stock("a and b"); + + rv.get_stock(v0).add_associated_edge({e0, v1}); + rv.get_stock(v1).add_associated_edge({e0, v0}); + re.get_stock(e0).set_associated_vertices(v0, v1); + + assert(re.get_stock(e0).is_associated(v0)); + assert(re.get_stock(e0).is_associated(v1)); + + auto const& s0 = rv.get_stock(v0).associated_edges(); + assert(s0.find({e0, v1}) != s0.end()); + + auto const& s1 = rv.get_stock(v1).associated_edges(); + assert(s1.find({e0, v0}) != s1.end()); +} +void white_box_test::test() +{ + test_vertex(); + test_edge(); + test_vertex_repository(); + test_vertex_edge_and_repository(); +} diff --git a/test/white_box_test.h b/test/white_box_test.h new file mode 100644 index 0000000..5e44ee8 --- /dev/null +++ b/test/white_box_test.h @@ -0,0 +1,13 @@ + +#ifndef GRAPH_WHITE_BOX_TEST_H +#define GRAPH_WHITE_BOX_TEST_H + + +class white_box_test +{ +public: + void test(); +}; + + +#endif //GRAPH_WHITE_BOX_TEST_H