From ea7bbca4bfac7cdf8a43924de9520ee03f60c338 Mon Sep 17 00:00:00 2001
From: yi-ji
Date: Sat, 28 Sep 2019 00:21:47 -0500
Subject: [PATCH 1/7] Link-Cut-Tree implementation and unit tests
---
include/boost/graph/link_cut_tree.hpp | 279 ++++++++++++++++++++++++++
test/Jamfile.v2 | 1 +
test/link_cut_tree_test.cpp | 170 ++++++++++++++++
3 files changed, 450 insertions(+)
create mode 100644 include/boost/graph/link_cut_tree.hpp
create mode 100644 test/link_cut_tree_test.cpp
diff --git a/include/boost/graph/link_cut_tree.hpp b/include/boost/graph/link_cut_tree.hpp
new file mode 100644
index 000000000..3e73ce884
--- /dev/null
+++ b/include/boost/graph/link_cut_tree.hpp
@@ -0,0 +1,279 @@
+//
+//=======================================================================
+// Copyright 2019
+// Author: Yi Ji
+//
+// 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_LINK_CUT_TREE_HPP
+#define BOOST_LINK_CUT_TREE_HPP
+
+#include
+#include
+
+namespace boost
+{
+ template
+ class link_cut_tree
+ {
+ public:
+ inline link_cut_tree(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {}
+
+ inline link_cut_tree(const link_cut_tree &c)
+ : parent(c.parent), left(c.left), right(c.right) {}
+
+ template
+ inline void make_tree(Element x)
+ {
+ make_path(x);
+ }
+
+ template
+ inline Element find_root(Element x)
+ {
+ return find_tail(expose(x));
+ }
+
+ template
+ inline void link(Element x, Element y) // Element x must be a tree root
+ {
+ Element r = expose(x);
+ r = join(r, r, expose(y));
+ put_successor(r, r);
+ }
+
+ template
+ inline void cut(Element x)
+ {
+ expose(x);
+ std::pair uv = split(x);
+ put_successor(x, x);
+ put_successor(uv.second, uv.second);
+ }
+
+ template
+ inline Element lowest_common_ancestor(Element x, Element y) // Elements x and y must have same root
+ {
+ expose(x);
+ return expose(y);
+ }
+
+ private:
+ ElementParentMap parent;
+ ElementChildMap left, right;
+
+ template
+ inline Element get_parent(Element x) const
+ {
+ Element x_parent = get(parent, x);
+ if (get(left, x_parent) == x || get(right, x_parent) == x)
+ return x_parent;
+ return x; // x_parent is actually x_successor when x has no parent
+ }
+
+ template
+ inline Element get_successor(Element x) const
+ {
+ return get(parent, x);
+ }
+
+ template
+ inline void put_successor(Element x, Element x_successor)
+ {
+ put(parent, x, x_successor);
+ }
+
+ template
+ inline void rotate(const Element x, const ElementChildMap &side)
+ {
+ const ElementChildMap &opposite = (&side == &left) ? right : left;
+ const Element pivot = get(side, x);
+ const Element x_parent = get_parent(x);
+ const Element pivot_side = get(opposite, pivot);
+
+ if (x_parent != x)
+ {
+ put(parent, pivot, x_parent);
+ put(get_side(x), x_parent, pivot);
+ }
+ else
+ {
+ Element x_successor = get_successor(x);
+ put_successor(pivot, x == x_successor ? pivot : x_successor);
+ }
+
+ if (pivot_side != pivot)
+ {
+ put(side, x, pivot_side);
+ put(parent, pivot_side, x);
+ }
+ else
+ put(side, x, x);
+
+ put(opposite, pivot, x);
+ put(parent, x, pivot);
+ }
+
+ template
+ inline ElementChildMap& get_side(Element x)
+ {
+ Element x_parent = get_parent(x);
+ if (get(left, x_parent) == x)
+ return left;
+ return right;
+ }
+
+ template
+ inline void splay(Element x)
+ {
+ for (Element x_parent = get_parent(x); x != x_parent; x_parent = get_parent(x))
+ {
+ const Element x_grandparent = get_parent(x_parent);
+ const ElementChildMap &x_side = get_side(x);
+ const ElementChildMap &x_parent_side = get_side(x_parent);
+
+ if (x_grandparent == x_parent)
+ rotate(x_parent, x_side);
+ else if (&x_side == &x_parent_side)
+ {
+ rotate(x_grandparent, x_parent_side);
+ rotate(x_parent, x_side);
+ }
+ else
+ {
+ rotate(x_parent, x_side);
+ rotate(x_grandparent, x_parent_side);
+ }
+ }
+ }
+
+ template
+ inline Element expose(Element x)
+ {
+ Element r = x;
+ while (true)
+ {
+ const Element x_successor = get_successor(find_path(x));
+ const std::pair uv = split(x);
+ if (x != uv.first)
+ put_successor(uv.first, x);
+ r = join(r, x, uv.second);
+ if (x == x_successor)
+ break;
+ x = x_successor;
+ }
+ put_successor(r, r);
+ return r;
+ }
+
+ template
+ inline void make_path(Element x)
+ {
+ put(parent, x, x);
+ put(right, x, x);
+ put(left, x, x);
+ }
+
+ template
+ inline Element find_path(Element x)
+ {
+ splay(x);
+ return x;
+ }
+
+ template
+ inline Element find_tail(Element x)
+ {
+ while (get(right, x) != x)
+ x = get(right, x);
+ splay(x);
+ return x;
+ }
+
+ template
+ inline Element join(Element u, Element v, Element w)
+ {
+ if (u != v)
+ put(parent, u, v);
+ if (w != v)
+ put(parent, w, v);
+ put(left, v, u);
+ put(right, v, w);
+ return v;
+ }
+
+ template
+ inline std::pair split(Element x)
+ {
+ splay(x);
+ Element x_left = get(left, x);
+ Element x_right = get(right, x);
+ if (x_left != x)
+ put(parent, x_left, x_left);
+ if (x_right != x)
+ put(parent, x_right, x_right);
+ put(left, x, x);
+ put(right, x, x);
+ return std::make_pair<>(x_left, x_right);
+ }
+ };
+
+
+ template
+ class link_cut_tree_with_storage :
+ public link_cut_tree::value_type, typename property_traits::value_type> > >
+ {
+ public:
+ typedef typename property_traits::key_type Vertex;
+ typedef typename property_traits::value_type Index;
+ typedef std::unordered_map IndexMapContainer;
+ typedef associative_property_map IndexMap;
+ typedef link_cut_tree LCT;
+
+ link_cut_tree_with_storage(ID id_ = ID()) :
+ LCT(IndexMap(parent_map), IndexMap(left_map), IndexMap(right_map)),
+ id(id_) {}
+
+ template
+ inline void make_tree(Vertex x)
+ {
+ const Index x_id = get(id, x);
+ LCT::make_tree(x_id);
+ id_to_vertex[x_id] = x;
+ }
+
+ template
+ inline Vertex find_root(Vertex x)
+ {
+ return id_to_vertex[LCT::find_root(get(id, x))];
+ }
+
+ template
+ inline void link(Vertex x, Vertex y)
+ {
+ LCT::link(get(id, x), get(id, y));
+ }
+
+ template
+ inline void cut(Vertex x)
+ {
+ LCT::cut(get(id, x));
+ }
+
+ template
+ inline Vertex lowest_common_ancestor(Vertex x, Vertex y)
+ {
+ return id_to_vertex[LCT::lowest_common_ancestor(get(id, x), get(id, y))];
+ }
+
+ private:
+ ID id;
+ std::unordered_map id_to_vertex;
+ IndexMapContainer parent_map, left_map, right_map;
+ };
+}
+
+#endif // BOOST_LINK_CUT_TREE_HPP
diff --git a/test/Jamfile.v2 b/test/Jamfile.v2
index b4fcfd4f3..b5f383762 100644
--- a/test/Jamfile.v2
+++ b/test/Jamfile.v2
@@ -112,6 +112,7 @@ alias graph_test_regular :
[ run king_ordering.cpp ]
[ run matching_test.cpp ]
[ run weighted_matching_test.cpp ]
+ [ run link_cut_tree_test.cpp ]
[ run max_flow_test.cpp ]
[ run boykov_kolmogorov_max_flow_test.cpp ]
[ run cycle_ratio_tests.cpp ../build//boost_graph ../../regex/build//boost_regex : $(CYCLE_RATIO_INPUT_FILE) ]
diff --git a/test/link_cut_tree_test.cpp b/test/link_cut_tree_test.cpp
new file mode 100644
index 000000000..3557de0df
--- /dev/null
+++ b/test/link_cut_tree_test.cpp
@@ -0,0 +1,170 @@
+//=======================================================================
+// Copyright (c) 2019 Yi Ji
+//
+// 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)
+//
+//=======================================================================
+
+#define BOOST_TEST_MODULE link_cut_tree_test
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
Revised
- 07
- October, 2019
+
+ June, 2020
diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp
index 00da5c2c7..217d3ec6e 100644
--- a/include/boost/graph/link_cut_trees.hpp
+++ b/include/boost/graph/link_cut_trees.hpp
@@ -22,7 +22,7 @@ namespace boost
public:
inline link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {}
- inline link_cut_trees(const link_cut_trees &c)
+ inline link_cut_trees(const link_cut_trees &c)
: parent(c.parent), left(c.left), right(c.right) {}
template
@@ -222,14 +222,15 @@ namespace boost
};
- template ::value_type, typename property_traits::key_type> >
+ template ::value_type, typename property_traits::key_type>,
+ class IndexMapContainer = boost::unordered_map::value_type, typename property_traits::value_type> >
class link_cut_trees_with_storage :
- public link_cut_trees::value_type, typename property_traits::value_type> > >
+ public link_cut_trees >
{
public:
typedef typename property_traits::key_type Vertex;
typedef typename property_traits::value_type Index;
- typedef boost::unordered_map IndexMapContainer;
typedef associative_property_map IndexMap;
typedef link_cut_trees LCT;
diff --git a/test/link_cut_trees_test.cpp b/test/link_cut_trees_test.cpp
index 855207483..9c4abed70 100644
--- a/test/link_cut_trees_test.cpp
+++ b/test/link_cut_trees_test.cpp
@@ -188,4 +188,26 @@ BOOST_AUTO_TEST_CASE(link_cut_trees_test5)
}
link_cut_trees_t lct(id, inverse_id);
test_link_cut_trees(lct, elements);
+}
+
+BOOST_AUTO_TEST_CASE(link_cut_trees_test6)
+{
+ typedef associative_property_map< std::map > id_map_t;
+ typedef vector_property_map inverse_id_map_t;
+ typedef link_cut_trees_with_storage > link_cut_trees_t;
+ std::vector elements;
+ std::vector numbers(100);
+ std::map id_map;
+ id_map_t id(id_map);
+ inverse_id_map_t inverse_id;
+ boost::range::iota(numbers, -49);
+ BOOST_FOREACH(int i, numbers)
+ {
+ std::string i_str = lexical_cast(i);
+ elements.push_back(i_str);
+ put(id, i_str, i+49);
+ put(inverse_id, i+49, i_str);
+ }
+ link_cut_trees_t lct(id, inverse_id);
+ test_link_cut_trees(lct, elements);
}
\ No newline at end of file
From 2c9585396f68f733421ee837f67d47986b007763 Mon Sep 17 00:00:00 2001
From: yi-ji
Date: Tue, 23 Jun 2020 17:52:25 +0900
Subject: [PATCH 5/7] Add same root assertion for `link` and
`lowest_common_ancestor`; Move typename outside of template
---
include/boost/graph/link_cut_trees.hpp | 6 ++++--
test/link_cut_trees_test.cpp | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp
index 217d3ec6e..7d7a4e6ae 100644
--- a/include/boost/graph/link_cut_trees.hpp
+++ b/include/boost/graph/link_cut_trees.hpp
@@ -38,8 +38,9 @@ namespace boost
}
template
- inline void link(Element x, Element y) // Element x must be a tree root
+ inline void link(Element x, Element y)
{
+ BOOST_ASSERT(find_root(x) == x); // Element x must be a tree root
Element r = expose(x);
r = join(r, r, expose(y));
put_successor(r, r);
@@ -55,8 +56,9 @@ namespace boost
}
template
- inline Element lowest_common_ancestor(Element x, Element y) // Elements x and y must have same root
+ inline Element lowest_common_ancestor(Element x, Element y)
{
+ BOOST_ASSERT(find_root(x) == find_root(y)); // Elements x and y must have same root
expose(x);
return expose(y);
}
diff --git a/test/link_cut_trees_test.cpp b/test/link_cut_trees_test.cpp
index 9c4abed70..b9279a833 100644
--- a/test/link_cut_trees_test.cpp
+++ b/test/link_cut_trees_test.cpp
@@ -194,7 +194,8 @@ BOOST_AUTO_TEST_CASE(link_cut_trees_test6)
{
typedef associative_property_map< std::map > id_map_t;
typedef vector_property_map inverse_id_map_t;
- typedef link_cut_trees_with_storage > link_cut_trees_t;
+ typedef std::map index_map_container_t;
+ typedef link_cut_trees_with_storage link_cut_trees_t;
std::vector elements;
std::vector numbers(100);
std::map id_map;
From 372d53fe216d61e4eec85bfd63511fe341e2191d Mon Sep 17 00:00:00 2001
From: yiji
Date: Wed, 24 Mar 2021 17:17:14 +0900
Subject: [PATCH 6/7] remove `inline` function modifiers
---
include/boost/graph/link_cut_trees.hpp | 58 ++++++++++++--------------
1 file changed, 26 insertions(+), 32 deletions(-)
diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp
index 7d7a4e6ae..d39444c08 100644
--- a/include/boost/graph/link_cut_trees.hpp
+++ b/include/boost/graph/link_cut_trees.hpp
@@ -20,25 +20,27 @@ namespace boost
class link_cut_trees
{
public:
- inline link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {}
+ link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {}
- inline link_cut_trees(const link_cut_trees &c)
+ link_cut_trees(const link_cut_trees &c)
: parent(c.parent), left(c.left), right(c.right) {}
template
- inline void make_tree(Element x)
+ void make_tree(Element x)
{
- make_path(x);
+ put(parent, x, x);
+ put(right, x, x);
+ put(left, x, x);
}
template
- inline Element find_root(Element x)
+ Element find_root(Element x)
{
return find_tail(expose(x));
}
template
- inline void link(Element x, Element y)
+ void link(Element x, Element y)
{
BOOST_ASSERT(find_root(x) == x); // Element x must be a tree root
Element r = expose(x);
@@ -47,7 +49,7 @@ namespace boost
}
template
- inline void cut(Element x)
+ void cut(Element x)
{
expose(x);
std::pair uv = split(x);
@@ -56,7 +58,7 @@ namespace boost
}
template
- inline Element lowest_common_ancestor(Element x, Element y)
+ Element lowest_common_ancestor(Element x, Element y)
{
BOOST_ASSERT(find_root(x) == find_root(y)); // Elements x and y must have same root
expose(x);
@@ -68,7 +70,7 @@ namespace boost
ElementChildMap left, right;
template
- inline Element get_parent(Element x) const
+ Element get_parent(Element x) const
{
Element x_parent = get(parent, x);
if (get(left, x_parent) == x || get(right, x_parent) == x)
@@ -77,19 +79,19 @@ namespace boost
}
template
- inline Element get_successor(Element x) const
+ Element get_successor(Element x) const
{
return get(parent, x);
}
template
- inline void put_successor(Element x, Element x_successor)
+ void put_successor(Element x, Element x_successor)
{
put(parent, x, x_successor);
}
template
- inline void rotate(const Element x, const ElementChildMap &side)
+ void rotate(const Element x, const ElementChildMap &side)
{
const ElementChildMap &opposite = (&side == &left) ? right : left;
const Element pivot = get(side, x);
@@ -120,7 +122,7 @@ namespace boost
}
template
- inline ElementChildMap& get_side(Element x)
+ ElementChildMap& get_side(Element x)
{
Element x_parent = get_parent(x);
if (get(left, x_parent) == x)
@@ -129,7 +131,7 @@ namespace boost
}
template
- inline void splay(Element x)
+ void splay(Element x)
{
for (Element x_parent = get_parent(x); x != x_parent; x_parent = get_parent(x))
{
@@ -153,7 +155,7 @@ namespace boost
}
template
- inline Element expose(Element x)
+ Element expose(Element x)
{
Element r = x;
while (true)
@@ -172,22 +174,14 @@ namespace boost
}
template
- inline void make_path(Element x)
- {
- put(parent, x, x);
- put(right, x, x);
- put(left, x, x);
- }
-
- template
- inline Element find_path(Element x)
+ Element find_path(Element x)
{
splay(x);
return x;
}
template
- inline Element find_tail(Element x)
+ Element find_tail(Element x)
{
while (get(right, x) != x)
x = get(right, x);
@@ -196,7 +190,7 @@ namespace boost
}
template
- inline Element join(Element u, Element v, Element w)
+ Element join(Element u, Element v, Element w)
{
if (u != v)
put(parent, u, v);
@@ -208,7 +202,7 @@ namespace boost
}
template
- inline std::pair split(Element x)
+ std::pair split(Element x)
{
splay(x);
Element x_left = get(left, x);
@@ -242,7 +236,7 @@ namespace boost
id_to_vertex(inverse_id) {}
template
- inline void make_tree(Vertex x)
+ void make_tree(Vertex x)
{
const Index x_id = get(id, x);
LCT::make_tree(x_id);
@@ -250,25 +244,25 @@ namespace boost
}
template
- inline Vertex find_root(Vertex x)
+ Vertex find_root(Vertex x)
{
return id_to_vertex[LCT::find_root(get(id, x))];
}
template
- inline void link(Vertex x, Vertex y)
+ void link(Vertex x, Vertex y)
{
LCT::link(get(id, x), get(id, y));
}
template
- inline void cut(Vertex x)
+ void cut(Vertex x)
{
LCT::cut(get(id, x));
}
template
- inline Vertex lowest_common_ancestor(Vertex x, Vertex y)
+ Vertex lowest_common_ancestor(Vertex x, Vertex y)
{
return id_to_vertex[LCT::lowest_common_ancestor(get(id, x), get(id, y))];
}
From 749c9a44b97121be5fb3859a1adb67b873ee69ee Mon Sep 17 00:00:00 2001
From: yi-ji
Date: Sun, 13 Jun 2021 00:41:15 +0900
Subject: [PATCH 7/7] Empty commit to trigger a build