diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml
index 37b52e177..aa7591576 100644
--- a/docs/DoxygenLayout.xml
+++ b/docs/DoxygenLayout.xml
@@ -50,8 +50,8 @@
-
-
+
+
diff --git a/docs/libsemigroups.bib b/docs/libsemigroups.bib
index b12adbb06..17a76cff1 100644
--- a/docs/libsemigroups.bib
+++ b/docs/libsemigroups.bib
@@ -7,6 +7,22 @@
%% Saved with string encoding Unicode (UTF-8)
+@article{Gab00,
+ Author = {Harold N. Gabow},
+ Doi = {https://dx.doi.org/10.1016/S0020-0190(00)00051-X},
+ Issn = {0020-0190},
+ Journal = {Information Processing Letters},
+ Keywords = {Algorithms},
+ Number = {34},
+ Pages = {107 - 114},
+ Title = {Path-based depth-first search for strong and biconnected
+ components},
+ Url = {https://www.sciencedirect.com/science/article/pii/S002001900000051X},
+ Volume = {74},
+ Year = {2000},
+ Bdsk-Url-2 = {https://dx.doi.org/10.1016/S0020-0190(00)00051-X},
+ Bdsk-Url-1 = {https://www.sciencedirect.com/science/article/pii/S002001900000051X},
+}
@misc{Anagnostopoulou-Merkouri2023aa,
author = {Anagnostopoulou-Merkouri, Marina and Mitchell, James D. and Tsalakou, Maria},
diff --git a/include/libsemigroups/action.tpp b/include/libsemigroups/action.tpp
index 226988363..1aecef54d 100644
--- a/include/libsemigroups/action.tpp
+++ b/include/libsemigroups/action.tpp
@@ -221,7 +221,7 @@ namespace libsemigroups {
if (started() && old_nr_gens < _gens.size()) {
// Generators were added after the last call to run
if (_pos > 0 && old_nr_gens < _gens.size()) {
- _scc.reset();
+ _scc.init(_graph);
}
for (size_t i = 0; i < _pos; i++) {
for (size_t j = old_nr_gens; j < _gens.size(); ++j) {
@@ -241,7 +241,7 @@ namespace libsemigroups {
}
}
if (_pos < _orb.size() && !_gens.empty()) {
- _scc.reset();
+ _scc.init(_graph);
}
detail::Timer t;
diff --git a/include/libsemigroups/cutting.hpp b/include/libsemigroups/cutting.hpp
index cd1888a45..adbd8e655 100644
--- a/include/libsemigroups/cutting.hpp
+++ b/include/libsemigroups/cutting.hpp
@@ -41,11 +41,33 @@
#ifndef LIBSEMIGROUPS_CUTTING_HPP_
#define LIBSEMIGROUPS_CUTTING_HPP_
-#include "cong-intf.hpp" // for CongruenceInterface
-#include "gabow.hpp"
-#include "runner.hpp" // for Runner
-#include "stephen.hpp"
-#include "types.hpp"
+#include // for for_each, copy
+#include // for size_t
+#include // for uint32_t, uin...
+#include // for shared_ptr
+#include // for operator+
+#include // for tie
+#include // for operator==
+#include // for swap, make_pair
+#include // for vector
+
+#include "constants.hpp" // for operator!=
+#include "gabow.hpp" // for Gabow
+#include "presentation.hpp" // for InversePresen...
+#include "ranges.hpp" // for begin, end
+#include "runner.hpp" // for Runner
+#include "stephen.hpp" // for Stephen
+#include "types.hpp" // for word_type
+#include "word-graph-with-sources.hpp" // for WordGraphWith...
+#include "word-graph.hpp" // for WordGraph
+#include "word-graph.hpp" // for standardize
+
+#include "detail/fmt.hpp" // for format, basic...
+#include "detail/int-range.hpp" // for IntRange<>::v...
+#include "detail/iterator.hpp" // for operator+
+#include "detail/node-managed-graph.hpp" // for NodeManagedGr...
+#include "detail/node-manager.hpp" // for NodeManager::...
+#include "detail/report.hpp" // for Ticker::Ticker
namespace libsemigroups {
class Cutting : public Runner {
@@ -69,7 +91,7 @@ namespace libsemigroups {
_stephens(),
_finished(false),
_graph(0, p.alphabet().size()),
- _gabow() {
+ _gabow(_graph) {
_presentation->validate();
_presentation->contains_empty_word(true); // TODO
_stephens.emplace_back(_presentation);
diff --git a/include/libsemigroups/gabow.hpp b/include/libsemigroups/gabow.hpp
index c1937a63b..490816de6 100644
--- a/include/libsemigroups/gabow.hpp
+++ b/include/libsemigroups/gabow.hpp
@@ -22,24 +22,55 @@
#ifndef LIBSEMIGROUPS_GABOW_HPP_
#define LIBSEMIGROUPS_GABOW_HPP_
-#include // for size_t
-#include // for queue
-#include // for stack
-#include // for vector
+#include // for size_t
+#include // for pair
+#include // for queue
+#include // for stack
+#include // for string
+#include // for vector
+#include "constants.hpp" // for UNDEFINED, operator!=, Undefined
+#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
+#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
#include "forest.hpp" // for Forest
+#include "ranges.hpp" // for iterator_range, transform
#include "word-graph.hpp" // for WordGraph
-#include "ranges.hpp" // for iterator_range, transform
-
namespace libsemigroups {
+ //! \defgroup gabow_group Gabow
+ //!
+ //! This page contains information about the implementation in
+ //! ``libsemigroups`` of Gabow's algorithm \cite Gab00 for computing the
+ //! strongly connected components of a WordGraph.
+
+ //! \ingroup gabow_group
+ //!
+ //! \brief Class implementing Gabow's algorithm for computing strongly
+ //! connected components of a WordGraph.
+ //!
+ //! Defined in ``gabow.hpp``.
+ //!
+ //! Instances of this class can be used to compute, and provide information
+ //! about, the strongly connected components of the WordGraph used to
+ //! construct the instance. The strongly connected components are
+ //! lazily evaluated when triggered by a relevant member function. The
+ //! complexity of Gabow's algorithm is at most \f$O(mn)\f$ where \c m is
+ //! WordGraph::number_of_nodes() and \c n is \ref WordGraph::out_degree().
+ //!
+ //! \tparam Node the type of the nodes of the underlying
+ //! WordGraph.
template
class Gabow {
public:
- using node_type = Node;
+ //! Type of the nodes in the underlying WordGraph.
+ using node_type = Node;
+
+ //! Type of the edge labels in the underlying WordGraph.
using label_type = typename WordGraph::label_type;
- using size_type = size_t;
+
+ //! Size type used for indices of strongly connected components.
+ using size_type = size_t;
private:
WordGraph const* _graph;
@@ -52,241 +83,409 @@ namespace libsemigroups {
mutable bool _forwd_forest_defined;
public:
- Gabow() = default;
- Gabow(Gabow const&) = default;
- Gabow(Gabow&&) = default;
+ //! \brief Deleted.
+ //!
+ //! To avoid the situation where the underlying WordGraph is not defined, it
+ //! is not possible to default construct a Gabow object.
+ Gabow() = delete;
+
+ //! \brief Default copy constructor.
+ //!
+ //! Default copy constructor.
+ Gabow(Gabow const&) = default;
+
+ //! \brief Default move constructor.
+ //!
+ //! Default move constructor.
+ Gabow(Gabow&&) = default;
+
+ //! \brief Default copy assignment operator.
+ //!
+ //! Default copy assignment operator.
Gabow& operator=(Gabow const&) = default;
- Gabow& operator=(Gabow&&) = default;
+
+ //! \brief Default move assignment operator.
+ //!
+ //! Default move assignment operator.
+ Gabow& operator=(Gabow&&) = default;
+
~Gabow();
- explicit Gabow(WordGraph const& wg) : Gabow() {
+ //! \brief Construct from WordGraph
+ //!
+ //! This function constructs a Gabow object from the WordGraph \p wg.
+ //!
+ //! \warning The Gabow object only holds a reference to the underlying
+ //! WordGraph \p wg, and so that object must outlive the corresponding Gabow
+ //! object.
+ //!
+ //! \note This function does not trigger the computation of the strongly
+ //! connected components.
+ explicit Gabow(WordGraph const& wg) {
init(wg);
}
+ //! \brief Reinitialize a Gabow object.
+ //!
+ //! This function re-initializes a Gabow object so that it is in the same
+ //! state as if it had just been constructed from \p wg.
+ //!
+ //! \returns A reference to `*this`.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ //!
+ //! \warning The Gabow object only holds a reference to the underlying
+ //! WordGraph \p wg, and so that object must outlive the corresponding Gabow
+ //! object.
+ //!
+ //! \note This function does not trigger the computation of the strongly
+ //! connected components.
Gabow& init(WordGraph const& wg);
- [[nodiscard]] size_type id_no_checks(node_type v) const {
+ //! \brief Get the id of a strongly connected component of a node.
+ //!
+ //! This function can be used to determine the id-number of a node in the
+ //! underlying graph of a Gabow instance.
+ //!
+ //! \param n the node.
+ //!
+ //! \returns The id-number of the strongly connected component of \p n.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ //!
+ //! \warning This function does not check that its argument \p n is actually
+ //! a node of the underlying word graph.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ [[nodiscard]] size_type id_no_checks(node_type n) const {
run();
- return _id[v];
+ return _id[n];
}
- //! Returns the id-number of the strongly connected component of a node.
+ //! \brief Returns the id-number of the strongly connected component of a
+ //! node.
//!
- //! \param nd the node.
+ //! This function can be used to determine the id-number of a node in the
+ //! underlying graph of a Gabow instance.
//!
- //! \returns
- //! The index of the node \p nd, a value of type scc_index_type.
+ //! \param n the node.
+ //!
+ //! \returns The id-number of the strongly connected component of \p n.
//!
- //! \throws LibsemigroupsException if \p nd is not valid.
+ //! \throws LibsemigroupsException if \p n is greater than or equal to
+ //! `word_graph().number_of_nodes()`.
//!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
// Not noexcept because validate_node isn't
- [[nodiscard]] size_type id(node_type v) const {
+ [[nodiscard]] size_type id(node_type n) const {
run();
- validate_node(v);
- return _id[v];
+ validate_node(n);
+ return id_no_checks(n);
}
- //! Returns an iterator pointing to the vector of nodes in the first scc.
+ //! \brief Returns a const reference to a vector of vectors containing the
+ //! strongly connected components.
//!
- //! \returns
- //! A \ref const_iterator_sccs.
+ //! This function returns a const reference to a vector of vectors
+ //! containing all of the strongly connected components of the WordGraph
+ //! (\ref word_graph) used to construct the Gabow instance.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl.
- //! \basic_guarantee
+ //! \returns
+ //! A vector of vectors of node_type.
//!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \exceptions
+ //! \no_libsemigroups_except
//!
- //! \par Parameters
- //! (None)
- [[nodiscard]] auto components() const {
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ [[nodiscard]] std::vector> const&
+ components() const {
run();
return _comps;
}
- [[nodiscard]] std::vector component(size_type i) const {
+ //! \brief Returns a const reference to a vector containing the strongly
+ //! connected component with given index.
+ //!
+ //! This function returns a const reference to a vector
+ //! containing the strongly connected components with index \p i of the
+ //! WordGraph (\ref word_graph) used to construct the Gabow instance.
+ //!
+ //! \param i the index of a strongly connected component.
+ //!
+ //! \returns A vector of node_type.
+ //!
+ //! \throws LibsemigroupsException if \p i is greater than or equal
+ //! to \ref number_of_components.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ //!
+ //! \sa \ref component_of to obtain the component of a node.
+ [[nodiscard]] std::vector const& component(size_type i) const {
run();
validate_scc_index(i);
return _comps[i];
}
+ //! \brief Returns a const reference to a vector containing the strongly
+ //! connected component with given index.
+ //!
+ //! This function returns a const reference to a vector
+ //! containing the strongly connected components with index \p i of the
+ //! WordGraph (\ref word_graph) used to construct the Gabow instance.
+ //!
+ //! \param i the index of a strongly connected component.
+ //!
+ //! \returns A vector of node_type.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ //!
+ //! \warning This function does not check that its argument \p i.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ //!
+ //! \sa \ref component_of_no_checks to obtain the strongly connected
+ //! component of a node.
[[nodiscard]] std::vector
component_no_checks(size_type i) const {
run();
return _comps[i];
}
- //! Returns the number of strongly connected components.
+ //! \brief Returns the number of strongly connected components.
+ //!
+ //! This function returns the number of strongly connected components of the
+ //! underlying WordGraph (returned by \ref word_graph).
//!
//! \returns
//! A `size_t`.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl.
- //! \basic_guarantee
- //!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
//!
- //! \par Parameters
- //! (None)
+ //! \exceptions
+ //! \no_libsemigroups_except
[[nodiscard]] size_t number_of_components() const {
run();
return _comps.size();
}
- //! Returns an iterator pointing to the root of the first scc.
+ //! \brief Returns a range object consisting of roots of the strongly
+ //! connected components.
//!
- //! \returns
- //! A \ref const_iterator_scc_roots.
+ //! This function returns a range object consisting of roots of the strongly
+ //! connected components.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl. \basic_guarantee
+ //! \returns
+ //! A range object by value.
//!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \exceptions
+ //! \no_libsemigroups_except
//!
- //! \par Parameters
- //! (None)
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ // TODO(0) add reference to range doc when available
[[nodiscard]] auto roots() const {
run();
return (rx::iterator_range(_comps.cbegin(), _comps.cend())
| rx::transform([](auto const& comp) { return comp[0]; }));
}
- //! Returns the root of a strongly connected components containing a given
- //! node.
+ //! \brief Returns the root of the strongly connected component containing a
+ //! given node.
//!
- //! \param nd a node.
+ //! This function returns the root of the strongly connected component
+ //! containing the node \p n of the underlying WordGraph. Two nodes \c a and
+ //! \c b belong to the same strongly connected component if and only if
+ //! `root_of(a) == root_of(b)`.
//!
- //! \returns
- //! The root of the scc containing the node \p nd, a value of
- //! \ref node_type.
- //!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl.
- //! \basic_guarantee
- //!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \param n the node.
+ //!
+ //! \returns The root of the strongly connected component containing the
+ //! node \p n, a value of \ref WordGraph::node_type.
+ //!
+ //! \throws LibsemigroupsException if \p n is greater than or equal to
+ //! WordGraph::number_of_nodes of the underlying word graph.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
// Not noexcept because scc_id isn't
[[nodiscard]] node_type root_of(node_type n) const {
return component_of(n)[0];
}
+ //! \brief Returns the root of the strongly connected component containing a
+ //! given node.
+ //!
+ //! This function returns the root of the strongly connected component
+ //! containing the node \p n of the underlying WordGraph. Two nodes \c a and
+ //! \c b belong to the same strongly connected component if and only if
+ //! `root_of_no_checks(a) == root_of_no_checks(b)`.
+ //!
+ //! \param n the node.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ //!
+ //! \returns
+ //! The root of the strongly connected component containing the node \p n,
+ //! a value of \ref WordGraph::node_type.
+ //!
+ //! \warning This function does not check that its argument \p n.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
[[nodiscard]] node_type root_of_no_checks(node_type n) const {
return component_of_no_checks(n)[0];
}
- //! Returns an iterator pointing to the first node in the scc with
- //! the specified id-number.
+ //! \brief Returns a const reference to a vector containing the strongly
+ //! connected component of a given node.
//!
- //! \param i the id-number of the scc.
+ //! This function returns a const reference to a vector
+ //! containing the strongly connected components of the node \p n of the
+ //! WordGraph (returned by \ref word_graph) used to construct the Gabow
+ //! instance.
//!
- //! \returns
- //! A \ref const_iterator_scc.
+ //! \param n the node.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl.
+ //! \returns A vector of node_type.
//!
- //! \throws LibsemigroupsException if \p i is not in the range \c 0 to \c
- //! number_of_scc() - 1.
+ //! \throws LibsemigroupsException if \p n is greater than or equal
+ //! to `word_graph().number_of_nodes()`.
//!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
- //!
- //! \note
- //! \basic_guarantee
- //!
- [[nodiscard]] std::vector component_of(node_type n) const {
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ [[nodiscard]] std::vector const&
+ component_of(node_type n) const {
run();
validate_node(n);
return _comps[_id[n]];
}
- [[nodiscard]] std::vector
+ //! \brief Returns a const reference to a vector containing the strongly
+ //! connected component of a given node.
+ //!
+ //! This function returns a const reference to a vector
+ //! containing the strongly connected components of the node \p n of the
+ //! WordGraph (returned by \ref word_graph) used to construct the Gabow
+ //! instance.
+ //!
+ //! \param n the node.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ //!
+ //! \returns A vector of node_type.
+ //!
+ //! \warning This function does not check that its argument \p n.
+ //!
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
+ [[nodiscard]] std::vector const&
component_of_no_checks(node_type n) const {
run();
return _comps[_id[n]];
}
- Gabow const& reset() const noexcept;
-
- //! Returns a spanning forest of the strongly connected components.
+ //! \brief Returns a spanning forest of the strongly connected components.
//!
- //! Returns a Forest comprised of spanning trees for each
- //! scc of \c this, rooted on the minimum node of that component, with
- //! edges oriented away from the root.
+ //! This function returns a Forest comprised of spanning trees for each
+ //! strongly connected component of a Gabow object, rooted on the minimum
+ //! node of that component, with edges oriented away from the root.
//!
//! \returns
//! A const reference to a Forest.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl. \basic_guarantee
- //!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
+ //! \exceptions
+ //! \no_libsemigroups_except
//!
- //! \par Parameters
- //! (None)
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
Forest const& spanning_forest() const;
- //! Returns a reverse spanning forest of the strongly connected components.
+ //! \brief Returns a reverse spanning forest of the strongly connected
+ //! components (if they are not already known).
//!
- //! Returns a Forest comprised of spanning trees for each
- //! scc of \c this, rooted on the minimum node of that component, with
- //! edges oriented towards the root.
+ //! This function returns a Forest comprised of spanning trees for each
+ //! strongly connected component of a Gabow object, rooted on the minimum
+ //! node of that component, with edges oriented towards the root.
//!
//! \returns
//! A const reference to a Forest.
//!
- //! \throws LibsemigroupsException if it is not the case that every node
- //! has exactly out_degree() out-targets. In other words, if
- //! target() is libsemigroups::UNDEFINED for any node \c nd and
- //! any label \c lbl. \basic_guarantee
+ //! \exceptions
+ //! \no_libsemigroups_except
//!
- //! \complexity
- //! At most \f$O(mn)\f$ where \c m is number_of_nodes() and \c n is
- //! out_degree().
- //!
- //! \par Parameters
- //! (None)
+ //! \note This function triggers the computation of the strongly connected
+ //! components (if they are not already known).
Forest const& reverse_spanning_forest() const;
- private:
- bool finished() const {
+ //! \brief Returns a const reference to the underlying word graph.
+ //!
+ //! This function returns a const reference to the underlying word graph.
+ //!
+ //! \returns
+ //! A const reference to a WordGraph.
+ //!
+ //! \exceptions
+ //! \noexcept
+ //!
+ //! \note This function does not trigger the computation of the strongly
+ //! connected components.
+ WordGraph const& word_graph() const noexcept {
+ return *_graph;
+ }
+
+ //! \brief Check whether the strongly connected components have been found.
+ //!
+ //! This function returns \c true if the strongly connected components of a
+ //! Gabow object have already been computed and \c false if not.
+ //!
+ //! \returns
+ //! A \c bool.
+ //!
+ //! \exceptions
+ //! \noexcept
+ [[nodiscard]] bool has_components() const noexcept {
return _finished;
}
+
+ private:
+ void reset() const noexcept;
void run() const;
void validate_node(node_type n) const;
void validate_scc_index(size_t i) const;
};
+ //! \ingroup gabow_group
+ //!
+ //! \brief Deduction guide for Gabow objects.
template
Gabow(WordGraph const&) -> Gabow;
+ //! \ingroup gabow_group
+ //!
+ //! \brief Return a human readable representation of a Gabow object.
+ //!
+ //! Return a human readable representation of a Gabow object.
+ //!
+ //! \tparam Node the type of the nodes in the underlying WordGraph
+ //!
+ //! \param g the Gabow object.
+ //!
+ //! \exceptions
+ //! \no_libsemigroups_except
+ template
+ std::string to_human_readable_repr(Gabow const& g);
+
} // namespace libsemigroups
#include "gabow.tpp"
diff --git a/include/libsemigroups/gabow.tpp b/include/libsemigroups/gabow.tpp
index 271b307ac..a88a3e2b7 100644
--- a/include/libsemigroups/gabow.tpp
+++ b/include/libsemigroups/gabow.tpp
@@ -29,12 +29,6 @@ namespace libsemigroups {
_id.clear();
_bckwd_forest.init();
_forwd_forest.init();
- reset();
- return *this;
- }
-
- template
- Gabow const& Gabow::reset() const noexcept {
_finished = false;
_bckwd_forest_defined = false;
_forwd_forest_defined = false;
@@ -127,7 +121,7 @@ namespace libsemigroups {
template
void Gabow::run() const {
- if (finished()) {
+ if (has_components()) {
return;
}
@@ -222,4 +216,20 @@ namespace libsemigroups {
i);
}
}
+
+ template
+ std::string to_human_readable_repr(Gabow const& g) {
+ std::string suffix = "";
+ if (g.has_components()) {
+ suffix = fmt::format("{} component{}",
+ g.number_of_components(),
+ g.number_of_components() != 1 ? "s" : "");
+ } else {
+ suffix = "components not yet found";
+ }
+ return fmt::format("",
+ g.word_graph().number_of_nodes(),
+ g.word_graph().number_of_nodes() != 1 ? "s" : "",
+ suffix);
+ }
} // namespace libsemigroups
diff --git a/tests/test-gabow.cpp b/tests/test-gabow.cpp
index 8ff4c8f9e..801b9103f 100644
--- a/tests/test-gabow.cpp
+++ b/tests/test-gabow.cpp
@@ -61,7 +61,7 @@ namespace libsemigroups {
Gabow scc(wg);
for (size_t j = 1; j < 100; ++j) {
wg.add_nodes(j);
- scc.reset();
+ scc.init(wg);
for (size_t i = 0; i < j * (j + 1) / 2; ++i) {
REQUIRE(scc.id(i) == i);
@@ -75,7 +75,7 @@ namespace libsemigroups {
Gabow scc(wg);
for (size_t j = 2; j < 50; ++j) {
word_graph::add_cycle(wg, j);
- scc.reset();
+ scc.init(wg);
REQUIRE((wg.nodes()
| filter([&scc, j](auto v) { return scc.id(v) == j - 2; })
| count())
@@ -247,7 +247,7 @@ namespace libsemigroups {
(wg.nodes() | all_of([&scc](node_type i) { return scc.id(i) == 0; })));
word_graph::add_cycle(wg, 10101);
- scc.reset();
+ scc.init(wg);
REQUIRE((wg.nodes() | take(100000)
| all_of([&scc](node_type i) { return scc.id(i) == 0; })));
REQUIRE((wg.nodes() | skip_n(100000)
@@ -269,7 +269,7 @@ namespace libsemigroups {
REQUIRE(wg.number_of_nodes() == 2 * n);
REQUIRE(wg.number_of_edges() == 2 * n * n);
- scc.reset();
+ scc.init(wg);
REQUIRE(scc.number_of_components() == 2);
auto expected = std::vector(n, 0);
@@ -340,4 +340,18 @@ namespace libsemigroups {
REQUIRE(scc.reverse_spanning_forest()
== to_forest({4, 2, 0, 4, UNDEFINED}, {2, 0, 1, 0, UNDEFINED}));
}
+
+ LIBSEMIGROUPS_TEST_CASE("Gabow",
+ "013",
+ "to_human_readable_repr",
+ "[quick][gabow]") {
+ auto wg = to_word_graph(
+ 5, {{0, 1, 4, 3}, {2}, {2, 0, 3, 3}, {4, 1}, {1, 0, 2}});
+ Gabow scc(wg);
+ REQUIRE(to_human_readable_repr(scc)
+ == "");
+ REQUIRE(scc.number_of_components() == 1);
+ REQUIRE(to_human_readable_repr(scc)
+ == "");
+ }
} // namespace libsemigroups