libsemigroups  v3.5.1
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
node-manager.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2019-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// This file contains the declaration for a class to manage nodes for
20// NodeManagedGraph instances.
21//
22// TODO(later) combine NodeManager and NodeManagedGraph
23// TODO(later) de-template this, we only ever use uint32_t for these so no point
24// in having it templated.
25
26#ifndef LIBSEMIGROUPS_DETAIL_NODE_MANAGER_HPP_
27#define LIBSEMIGROUPS_DETAIL_NODE_MANAGER_HPP_
28
29#include <cstddef> // for size_t
30#include <cstdint> // for uint32_t
31#include <numeric> // for iota
32#include <type_traits> // for true_type
33#include <vector> // for vector
34
35#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_DEBUG
36#include "libsemigroups/constants.hpp" // for UNDEFINED
37#include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_ASSERT/DEBUG
38#include "libsemigroups/exception.hpp" // for LISEMIGROUPS_EXCEPTION
39#include "libsemigroups/ranges.hpp"
40
41namespace libsemigroups {
42 namespace detail {
43 template <typename Node>
44 class NodeManager {
45 public:
47 // NodeManager - typedefs - public
49
50 using node_type = Node;
51 using Perm = std::vector<node_type>;
52
54 // NodeManager - data - protected
56 protected:
57 node_type _current;
58 node_type _current_la;
59
61 // NodeManager - data - private
63 private:
64 static constexpr node_type _id_node = 0;
65
66 std::vector<node_type> _bckwd;
67 node_type _first_free_node;
68 std::vector<node_type> _forwd;
69 float _growth_factor;
70 mutable std::vector<node_type> _ident;
71 node_type _last_active_node;
72
73 struct Stats {
74 std::atomic_uint64_t num_nodes_active;
75 std::atomic_uint64_t num_nodes_defined;
76 std::atomic_uint64_t num_nodes_killed;
77
78 Stats() : num_nodes_active(), num_nodes_defined(), num_nodes_killed() {
79 init();
80 }
81
82 Stats& init() {
83 num_nodes_active = 1;
84 num_nodes_defined = 1;
85 num_nodes_killed = 0;
86 return *this;
87 }
88
89 Stats(Stats const& that)
90 : num_nodes_active(that.num_nodes_active.load()),
91 num_nodes_defined(that.num_nodes_defined.load()),
92 num_nodes_killed(that.num_nodes_killed.load()) {}
93
94 Stats(Stats&& that)
95 : num_nodes_active(that.num_nodes_active.load()),
96 num_nodes_defined(that.num_nodes_defined.load()),
97 num_nodes_killed(that.num_nodes_killed.load()) {}
98
99 Stats& operator=(Stats const& that) {
100 num_nodes_active = that.num_nodes_active.load();
101 num_nodes_defined = that.num_nodes_defined.load();
102 num_nodes_killed = that.num_nodes_killed.load();
103 return *this;
104 }
105
106 Stats& operator=(Stats&& that) {
107 num_nodes_active = that.num_nodes_active.load();
108 num_nodes_defined = that.num_nodes_defined.load();
109 num_nodes_killed = that.num_nodes_killed.load();
110 return *this;
111 }
112 } _stats;
113
114 public:
116 // NodeManager - constructors + destructor - public
118
119 NodeManager();
120 NodeManager& init();
121
122 NodeManager(NodeManager const& that);
123 NodeManager(NodeManager&&) = default;
124
125 NodeManager& operator=(NodeManager const& that);
126 NodeManager& operator=(NodeManager&&) = default;
127
128 ~NodeManager();
129
131 // NodeManager - member functions - public
133
134 node_type& cursor() {
135 return _current;
136 }
137
138 node_type& lookahead_cursor() {
139 return _current_la;
140 }
141
142 node_type const& lookahead_cursor() const {
143 return _current_la;
144 }
145
146 // std::vector::size is noexcept
147 inline size_t node_capacity() const noexcept {
148 return _forwd.size();
149 }
150
151 inline node_type first_free_node() const noexcept {
152 return _first_free_node;
153 }
154
155 inline bool has_free_nodes() const noexcept {
156 return _first_free_node != UNDEFINED;
157 }
158
159 // not noexcept since std::vector::operator[] isn't.
160 inline bool is_active_node(node_type c) const {
161 LIBSEMIGROUPS_ASSERT(c < _ident.size() || c == UNDEFINED);
162 return c != UNDEFINED && _ident[c] == c;
163 }
164
165 [[nodiscard]] size_t position_of_node(node_type n) const;
166
167 inline bool is_valid_node(node_type c) const noexcept {
168 return c < _forwd.size();
169 }
170
171 // not noexcept since std::vector::operator[] isn't.
172 inline node_type next_active_node(node_type c) const {
173 return _forwd[c];
174 }
175
176 // Note that these iterators are invalidated by any changes to the
177 // container, i.e. they can't be used as a substitute for cursors.
178 auto cbegin_active_nodes() const {
179 return rx::begin(ActiveNodesRange(this));
180 }
181
182 // Note that these iterators are invalidated by any changes to the
183 // container, i.e. they can't be used as a substitute for cursors.
184 auto cend_active_nodes() const {
185 return rx::end(ActiveNodesRange(this));
186 }
187
188 inline uint64_t number_of_nodes_active() const noexcept {
189 return _stats.num_nodes_active;
190 }
191
192 inline uint64_t number_of_nodes_defined() const noexcept {
193 return _stats.num_nodes_defined;
194 }
195
196 inline uint64_t number_of_nodes_killed() const noexcept {
197 return _stats.num_nodes_killed;
198 }
199
200 NodeManager& growth_factor(float val);
201
202 float growth_factor() const noexcept {
203 return _growth_factor;
204 }
205
207 // NodeManager - member functions - protected
209
210 // not noexcept because free_node isn't, and std::vector::operator[]
211 // isn't.
212
213 inline void union_nodes(node_type min, node_type max) {
214 LIBSEMIGROUPS_ASSERT(is_active_node(min));
215 LIBSEMIGROUPS_ASSERT(is_active_node(max));
216 LIBSEMIGROUPS_ASSERT(max > min);
217 free_node(max);
218 // Leave a "forwarding address" so we know what <max> was identified
219 // with
220 _ident[max] = min;
221 }
222
223 // not noexcept since std::vector::operator[] isn't.
224
225 inline node_type find_node(node_type c) const {
226 LIBSEMIGROUPS_ASSERT(is_valid_node(c));
227 while (true) {
228 node_type d = _ident[c];
229 if (d == c) {
230 return d;
231 }
232 node_type e = _ident[d];
233 if (d == e) {
234 return d;
235 }
236 _ident[c] = e;
237 c = e;
238 }
239 LIBSEMIGROUPS_ASSERT(is_active_node(c));
240 return c;
241 }
242
243 void add_active_nodes(size_t);
244 void add_free_nodes(size_t);
245 void erase_free_nodes();
246 node_type new_active_node();
247
248 // TODO rename -> swap_nodes_no_checks
249 void switch_nodes(node_type const, node_type const);
250 // TODO rename -> permute_nodes_no_checks
251 void apply_permutation(Perm p);
252
253 void clear();
254 void compact();
255
256 // not noexcept since std::vector::operator[] isn't.
257 void free_node(node_type const);
258
259 constexpr node_type initial_node() const noexcept {
260 return _id_node;
261 }
262
263 [[nodiscard]] node_type max_active_node() const noexcept;
264
265 private:
266 void compact(size_t N);
267
268 struct ActiveNodesRange {
269 using output_type = node_type;
270
271 NodeManager const* _node_manager;
272 output_type _current;
273
274 ActiveNodesRange() : _node_manager(nullptr), _current(UNDEFINED) {}
275
276 explicit ActiveNodesRange(NodeManager const* nm)
277 : _node_manager(nm), _current(nm->initial_node()) {}
278
279 output_type get() const noexcept {
280 return _current;
281 }
282
283 void next() noexcept {
284 _current = _node_manager->next_active_node(_current);
285 }
286
287 bool at_end() const noexcept {
288 return _node_manager == nullptr
289 || _current == _node_manager->first_free_node();
290 }
291
292 size_t size_hint() const noexcept {
293 return _node_manager->number_of_nodes_active();
294 }
295
296 static constexpr bool is_finite = true;
297 static constexpr bool is_idempotent = true;
298 };
299
300 public:
301 ActiveNodesRange active_nodes() const {
302 return ActiveNodesRange(this);
303 }
304
305#ifdef LIBSEMIGROUPS_DEBUG
306
307 protected:
309 // NodeManager - member functions (debug only) - protected
311 void debug_validate_forwd_bckwd() const;
312#endif
313 };
314
315 } // namespace detail
316} // namespace libsemigroups
317
318#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
319// TODO replace uint32_t by Node
320template <>
321struct rx::is_input_range<
322 typename libsemigroups::detail::NodeManager<uint32_t>::ActiveNodesRange>
323 : std::true_type {};
324#endif
325#include "node-manager.tpp"
326
327#endif // LIBSEMIGROUPS_DETAIL_NODE_MANAGER_HPP_
Undefined const UNDEFINED
Value for something undefined.
T max(T... args)
T min(T... args)
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T next(T... args)