libsemigroups  v3.0.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
dot.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2023-2025 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19#ifndef LIBSEMIGROUPS_DOT_HPP_
20#define LIBSEMIGROUPS_DOT_HPP_
21
22#include <array> // for array
23#include <cstddef> // for size_t
24#include <map> // for map, operator!=, __map_const_...
25#include <string> // for basic_string, operator<, oper...
26#include <string_view> // for basic_string_view
27#include <utility> // for forward, move etc
28#include <vector> // for vector
29
30#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
31#include "libsemigroups/bipart.hpp"
32#include "ranges.hpp" // for LIBSEMIGROUPS_EXCEPTION
33
34#include "detail/fmt.hpp" // for format
35#include "detail/report.hpp" // for LIBSEMIGROUPS_EXCEPTION
36
37namespace libsemigroups {
38
39#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
40 namespace detail {
41
42 inline std::string const& dot_to_string(std::string const& x) {
43 return x;
44 }
45
46 inline std::string& dot_to_string(std::string& x) {
47 return x;
48 }
49
50 inline std::string&& dot_to_string(std::string&& x) {
51 return std::move(x);
52 }
53
54 inline std::string dot_to_string(char const* x) {
55 return std::string(x);
56 }
57
58 inline std::string dot_to_string(std::string_view x) {
59 return std::string(x);
60 }
61
62 template <typename Thing>
63 std::string dot_to_string(Thing&& thing) {
65 }
66
67 } // namespace detail
68
69#endif // LIBSEMIGROUPS_PARSED_BY_DOXYGEN
70
91
115 class Dot {
116 public:
122 struct Node {
125
128
141 template <typename Thing>
142 explicit Node(Thing&& thing)
143 : attrs(), name(detail::dot_to_string(std::forward<Thing>(thing))) {}
144
146 Node() = default;
147
149 Node(Node const&) = default;
150
152 Node(Node&&) = default;
153
155 Node& operator=(Node const&) = default;
156
158 Node& operator=(Node&&) = default;
159
160 ~Node();
161
183 template <typename Thing1, typename Thing2>
184 Node& add_attr(Thing1&& key, Thing2&& val) {
185 auto key_str = detail::dot_to_string(std::forward<Thing1>(key));
186 auto val_str = detail::dot_to_string(std::forward<Thing2>(val));
187
188 add_or_replace_attr(attrs, key_str, val_str);
189 return *this;
190 }
191 };
192
198 struct Edge {
212 template <typename Thing1, typename Thing2>
213 Edge(Thing1&& h, Thing2&& t)
214 : attrs(),
215 head(detail::dot_to_string(std::forward<Thing1>(h))),
216 tail(detail::dot_to_string(std::forward<Thing2>(t))) {}
217
239 template <typename Thing1, typename Thing2>
240 Edge& add_attr(Thing1&& key, Thing2&& val) {
241 auto key_str = detail::dot_to_string(std::forward<Thing1>(key));
242 auto val_str = detail::dot_to_string(std::forward<Thing2>(val));
243
244 add_or_replace_attr(attrs, key_str, val_str);
245 return *this;
246 }
247
249 Edge() = default;
250
252 Edge(Edge const&) = default;
253
255 Edge(Edge&&) = default;
256
258 Edge& operator=(Edge const&) = default;
259
261 Edge& operator=(Edge&&) = default;
262
263 ~Edge();
264
267
270
273 };
274
276 enum class Kind {
284 };
285
290 = {"#00ff00", "#ff00ff", "#007fff", "#ff7f00", "#7fbf7f", "#4604ac",
291 "#de0328", "#19801d", "#d881f5", "#00ffff", "#ffff00", "#00ff7f",
292 "#ad5867", "#85f610", "#84e9f5", "#f5c778", "#207090", "#764ef3",
293 "#7b4c00", "#0000ff", "#b80c9a", "#601045", "#29b7c0", "#839f12"};
294
295 private:
297 std::vector<Edge> _edges;
298 Kind _kind;
299 std::string _name;
301 std::vector<Dot> _subgraphs;
302
303 public:
306
308 Dot(Dot const&);
309
312
314 Dot& operator=(Dot const&);
315
318
319 ~Dot();
320
329 Dot& kind(Kind val) noexcept {
330 _kind = val;
331 return *this;
332 }
333
340 Kind kind() const noexcept {
341 return _kind;
342 }
343
349 Dot& name(std::string const& val) {
350 _name = val;
351 return *this;
352 }
353
359 std::string const& name() const noexcept {
360 return _name;
361 }
362
372 auto nodes() noexcept {
373 return rx::iterator_range(_nodes.begin(), _nodes.end())
374 | rx::transform([](auto& pair) -> Node& { return pair.second; });
375 }
376
386 auto nodes() const noexcept {
387 return rx::iterator_range(_nodes.begin(), _nodes.end())
388 | rx::transform(
389 [](auto& pair) -> Node const& { return pair.second; });
390 }
391
402 return _edges;
403 }
404
414 std::vector<Edge> const& edges() const noexcept {
415 return _edges;
416 }
417
429 return _subgraphs;
430 }
431
442 std::vector<Dot> const& subgraphs() const noexcept {
443 return _subgraphs;
444 }
445
457 return _attrs;
458 }
459
481
484
506 template <typename Thing1, typename Thing2>
507 Dot& add_attr(Thing1&& key, Thing2&& val) {
508 auto key_str = detail::dot_to_string(std::forward<Thing1>(key));
509 auto val_str = detail::dot_to_string(std::forward<Thing2>(val));
510
511 add_or_replace_attr(_attrs, key_str, val_str);
512 return *this;
513 }
514
534 template <typename Thing>
535 Dot& add_attr(Thing&& key) {
536 add_or_replace_attr(_attrs, key, "");
537 return *this;
538 }
539
548 [[nodiscard]] bool is_node(std::string const& name) const {
549 return _nodes.count(name);
550 }
551
563 template <typename Thing,
564 typename std::enable_if_t<
565 !std::is_same_v<std::decay_t<Thing>, std::string>>>
566 [[nodiscard]] bool is_node(Thing&& thing) const {
567 return is_node(detail::dot_to_string(std::forward<Thing>(thing)));
568 }
569
585 template <typename Thing>
586 Node& add_node(Thing&& thing) {
587 auto name_str = detail::dot_to_string(std::forward<Thing>(thing));
588 auto [it, inserted] = _nodes.emplace(name_str, Node(name_str));
589 if (!inserted) {
590 LIBSEMIGROUPS_EXCEPTION("there is already a node named {}!", name_str);
591 }
592 return it->second;
593 }
594
610 template <typename Thing>
611 Node& node(Thing&& thing) {
612 auto name_str = detail::dot_to_string(std::forward<Thing>(thing));
613 auto it = _nodes.find(name_str);
614 if (it == _nodes.cend()) {
615 LIBSEMIGROUPS_EXCEPTION("there is no node named {}!", name_str);
616 }
617 return it->second;
618 }
619
637 template <typename Thing1, typename Thing2>
638 Edge& add_edge(Thing1&& head, Thing2&& tail) {
639 auto head_str = detail::dot_to_string(std::forward<Thing1>(head));
640 auto tail_str = detail::dot_to_string(std::forward<Thing2>(tail));
641 throw_if_not_node(head_str);
642 throw_if_not_node(tail_str);
643 _edges.emplace_back(head_str, tail_str);
644 return _edges.back();
645 }
646
667 template <typename Thing1, typename Thing2>
668 Edge& edge(Thing1&& head, Thing2&& tail) {
669 auto head_str = detail::dot_to_string(std::forward<Thing1>(head));
670 auto tail_str = detail::dot_to_string(std::forward<Thing2>(tail));
671 throw_if_not_node(head_str);
672 throw_if_not_node(tail_str);
673 auto it = std::find_if(_edges.begin(), _edges.end(), [&](Edge const& e) {
674 return e.head == head_str && e.tail == tail_str;
675 });
676 if (it == _edges.cend()) {
678 "there is no edges from {} to {}!", head_str, tail_str);
679 }
680 return *it;
681 }
682
694
695 private:
696 void throw_if_not_node(std::string const& s);
697
698 std::string_view edge_string() const noexcept;
699
700 static void add_or_replace_attr(std::map<std::string, std::string>& attrs,
701 std::string const& key,
702 std::string const& val);
703 }; // class Dot
704
715 std::string to_human_readable_repr(Dot const& d);
716
727 std::string to_human_readable_repr(Dot::Node const& n);
728
739 std::string to_human_readable_repr(Dot::Edge const& e);
740
753 std::string const& sep = "::");
754
755} // namespace libsemigroups
756#endif // LIBSEMIGROUPS_DOT_HPP_
bool is_node(Thing &&thing) const
Check if there is a node with name obtained from an object.
Definition dot.hpp:566
auto nodes() noexcept
Returns a range object of references to the current nodes.
Definition dot.hpp:372
Dot & add_subgraph(Dot const &subgraph)
Add a Dot object as a subgraph.
auto nodes() const noexcept
Returns a range object of const references to the current nodes.
Definition dot.hpp:386
std::vector< Dot > const & subgraphs() const noexcept
Returns a const reference to the vector of subgraphs.
Definition dot.hpp:442
std::string to_human_readable_repr(Dot const &d)
Return a human readable representation of a Dot object.
Dot & add_attr(Thing &&key)
Add an attribute to the graph.
Definition dot.hpp:535
Node & node(Thing &&thing)
Return a node from the represented graph.
Definition dot.hpp:611
std::vector< Edge > & edges() noexcept
Returns a reference to the vector of edges.
Definition dot.hpp:401
Dot & add_subgraph(Dot &&subgraph)
Add a Dot object as a subgraph.
Dot & operator=(Dot &&)
Default move assignment operator.
static constexpr std::array< std::string_view, 24 > colors
An array of default HTML/hex colours.
Definition dot.hpp:290
Dot & add_attr(Thing1 &&key, Thing2 &&val)
Add an attribute to the graph.
Definition dot.hpp:507
Dot(Dot &&)
Default move constructor.
Dot(Dot const &)
Default copy constructor.
std::map< std::string, std::string > const & attrs() const noexcept
Returns a const reference to the map of attributes.
Definition dot.hpp:456
Node & add_node(Thing &&thing)
Add a node to the represented graph.
Definition dot.hpp:586
Dot()
Default constructor.
Dot & kind(Kind val) noexcept
Set the kind of the represented graph.
Definition dot.hpp:329
Edge & add_edge(Thing1 &&head, Thing2 &&tail)
Add an edge with given head and tail.
Definition dot.hpp:638
Dot & name(std::string const &val)
Set the name of the represented graph.
Definition dot.hpp:349
Dot & operator=(Dot const &)
Default copy assignment operator.
Kind
The kind of object represented.
Definition dot.hpp:276
@ subgraph
Definition dot.hpp:283
@ digraph
Value indicating that the represented graph has directed edges ->.
Definition dot.hpp:278
@ graph
Value indicating that the represented graph has undirected edges --.
Definition dot.hpp:280
std::string to_string() const
Convert a Dot object to a string.
bool is_node(std::string const &name) const
Check if there is a node with a given name.
Definition dot.hpp:548
std::vector< Dot > & subgraphs() noexcept
Returns a reference to the vector of subgraphs.
Definition dot.hpp:428
std::string const & name() const noexcept
Get the current name of the represented graph.
Definition dot.hpp:359
Edge & edge(Thing1 &&head, Thing2 &&tail)
Returns the first edge with given head and tail.
Definition dot.hpp:668
std::vector< Edge > const & edges() const noexcept
Returns a const reference to the vector of edges.
Definition dot.hpp:414
Kind kind() const noexcept
Get the kind of the represented graph.
Definition dot.hpp:340
T find_if(T... args)
T forward(T... args)
#define LIBSEMIGROUPS_EXCEPTION(...)
Throw a LibsemigroupsException.
Definition exception.hpp:95
T move(T... args)
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
This nested struct represents an edge in the represented graph.
Definition dot.hpp:198
std::string head
Name of the head of the edge.
Definition dot.hpp:269
Edge(Thing1 &&h, Thing2 &&t)
Construct from head and tail.
Definition dot.hpp:213
Edge & add_attr(Thing1 &&key, Thing2 &&val)
Add an attribute to an edge.
Definition dot.hpp:240
std::map< std::string, std::string > attrs
Map of attributes.
Definition dot.hpp:266
Edge & operator=(Edge &&)=default
Default move assignment.
std::string tail
Name of the tail of the edge.
Definition dot.hpp:272
std::string to_human_readable_repr(Dot::Edge const &e)
Return a human readable representation of a Dot::Edge object.
Edge & operator=(Edge const &)=default
Default copy assignment.
Edge()=default
Default constructor.
Edge(Edge &&)=default
Default move constructor.
Edge(Edge const &)=default
Default copy constructor.
This nested struct represents a node in the represented graph.
Definition dot.hpp:122
Node & operator=(Node &&)=default
Default move assignment.
Node(Node &&)=default
Default move constructor.
Node(Thing &&thing)
Construct from anything.
Definition dot.hpp:142
Node()=default
Default constructor.
std::map< std::string, std::string > attrs
Map containing the attributes of the Node.
Definition dot.hpp:124
std::string name
The name of the node.
Definition dot.hpp:127
Node(Node const &)=default
Default copy constructor.
Node & operator=(Node const &)=default
Default copy assignment.
std::string to_human_readable_repr(Dot::Node const &n)
Return a human readable representation of a Dot::Node object.
Node & add_attr(Thing1 &&key, Thing2 &&val)
Add an attribute to a node.
Definition dot.hpp:184
T to_string(T... args)