LAL: Linear Arrangement Library 23.01.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
Dmin_Unconstrained_FC.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2023
7 *
8 * This file is part of Linear Arrangement Library. The full code is available
9 * at:
10 * https://github.com/LAL-project/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * Juan Luis Esteban (esteban@cs.upc.edu)
28 * LOGPROG: Logics and Programming Research Group
29 * Office 110, Omega building
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://www.cs.upc.edu/~esteban/
32 *
33 * LluĂ­s Alemany Puig (lalemany@cs.upc.edu)
34 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
37 * Webpage: https://cqllab.upc.edu/people/lalemany/
38 *
39 ********************************************************************/
40
41// C++ includes
42#if defined DEBUG
43#include <cassert>
44#endif
45
46#include <optional>
47#include <limits>
48#include <vector>
49
50#include <lal/linear_arrangement.hpp>
51#include <lal/detail/pairs_utils.hpp>
52#include <lal/detail/graphs/traversal.hpp>
53#include <lal/detail/properties/tree_centroid.hpp>
54#include <lal/detail/graphs/size_subtrees.hpp>
55#include <lal/detail/sorting/counting_sort.hpp>
56#include <lal/detail/data_array.hpp>
57#include <lal/detail/macros/basic_convert.hpp>
58#include <lal/detail/linarr/Dopt_utils.hpp>
59
60namespace lal {
61namespace detail {
62namespace Dmin {
63namespace unconstrained {
64
71namespace Chung {
72using namespace Dopt_utils;
73
76
77/*
78int calculate_q(uint64_t n, const ordering& ord) {
79 const uint64_t k = to_uint64(ord.size()) - 1;
80 const uint64_t t_0 = ord[0].size;
81
82 // Maximum possible p_alpha
83 int64_t q = to_int64(k)/2;
84 uint64_t sum = 0;
85 for (uint64_t i = 0; i <= 2*to_uint64(q); ++i) {
86 sum += ord[i].size;
87 }
88
89 uint64_t z = n - sum;
90 uint64_t tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
91 // t_0 >= t_1 >= ... >= t_k
92 uint64_t t_2q = ord[2*q].size;
93 while (q >= 0 and t_2q <= tricky_formula) {
94 z += ord[2*q].size;
95 if (q > 0) {
96 z += ord[2*q - 1].size;
97 }
98 --q;
99 tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
100 if (q >= 0) {
101 t_2q = ord[2*q].size;
102 }
103 }
104 return q;
105}
106*/
107
116std::optional<uint64_t> calculate_q(uint64_t n, const ordering& ord) noexcept {
117#if defined DEBUG
118 assert(ord.size() > 0);
119#endif
120
121 const uint64_t k = ord.size() - 1;
122 const uint64_t t_0 = ord[0].size;
123
124 // Maximum possible p_alpha
125 uint64_t q = k/2;
126 uint64_t sum = 0;
127 for (uint64_t i = 0; i <= 2*q; ++i) {
128 sum += ord[i].size;
129 }
130
131 uint64_t z = n - sum;
132 uint64_t tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
133 // t_0 >= t_1 >= ... >= t_k
134 uint64_t t_2q = ord[2*q].size;
135
136 while (t_2q <= tricky_formula) {
137 z += ord[2*q].size;
138
139 if (q > 0) { z += ord[2*q - 1].size; }
140 tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
141
142 if (q == 0) { return {}; }
143 --q;
144 t_2q = ord[2*q].size;
145 }
146 return q;
147}
148
149/*
150int calculate_p(uint64_t n, const ordering& ord) {
151 if (ord.size() < 2) {
152 return -1;
153 }
154
155 // number of subtrees (T_0, T_1, ..., T_k)
156 const uint64_t k = to_uint64(ord.size() - 1);
157 const uint64_t t_0 = ord[0].size;
158
159 int p = (k - 1)/2;
160
161 uint64_t sum = 0;
162 for (uint64_t i = 0; i <= 2*to_uint64(p) + 1; ++i) {
163 sum += ord[i].size;
164 }
165
166 uint64_t y = n - sum;
167 uint64_t tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
168 uint64_t t_2p_plus_1 = ord[2*p + 1].size;
169
170 while (p >= 0 and t_2p_plus_1 <= tricky_formula) {
171 y = y + ord[2*p + 1].size + ord[2*p].size;
172 --p;
173 tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
174 if (p >= 0) {
175 t_2p_plus_1 = ord[2*p + 1].size;
176 }
177 }
178 return p;
179}
180*/
181
190std::optional<uint64_t> calculate_p(uint64_t n, const ordering& ord) noexcept {
191 if (ord.size() < 2) { return {}; }
192
193 // number of subtrees (T_0, T_1, ..., T_k)
194 const uint64_t k = ord.size() - 1;
195 const uint64_t t_0 = ord[0].size;
196
197 uint64_t p = (k - 1)/2;
198
199 uint64_t sum = 0;
200 for (uint64_t i = 0; i <= 2*p + 1; ++i) {
201 sum += ord[i].size;
202 }
203
204 uint64_t y = n - sum;
205 uint64_t tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
206 uint64_t t_2p_plus_1 = ord[2*p + 1].size;
207
208 while (t_2p_plus_1 <= tricky_formula) {
209 y = y + ord[2*p + 1].size + ord[2*p].size;
210 tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
211
212 if (p == 0) { return {}; }
213 --p;
214 t_2p_plus_1 = ord[2*p + 1].size;
215 }
216 return p;
217}
218
227data_array<uint64_t> get_P(uint64_t p, uint64_t i) noexcept {
228 data_array<uint64_t> v(2*p + 1 + 1);
229 uint64_t pos = v.size() - 1;
230 uint64_t right_pos = pos;
231 uint64_t left_pos = 1;
232
233 uint64_t j = 0;
234 while (j <= 2*p + 1) {
235 if (j == i) {
236 ++j;
237 }
238 else {
239 v[pos] = j;
240 if (pos > left_pos) {
241 --right_pos;
242 pos = left_pos;
243 }
244 else {
245 ++left_pos;
246 pos = right_pos;
247 }
248 ++j;
249 }
250 }
251
252 return v;
253}
254
263data_array<uint64_t> get_Q(uint64_t q, uint64_t i) noexcept {
264 data_array<uint64_t> v(2*q + 1);
265 uint64_t pos = v.size() - 1;
266 uint64_t right_pos = pos;
267 uint64_t left_pos = 1;
268
269 uint64_t j = 0;
270 while (j <= 2*q) {
271 if (j == i) {
272 ++j;
273 }
274 else {
275 v[pos] = j;
276 if (pos > left_pos) {
277 --right_pos;
278 pos = left_pos;
279 }
280 else {
281 ++left_pos;
282 pos = right_pos;
283 }
284 ++j;
285 }
286 }
287 return v;
288}
289
299 // Let 'T_u' to be a tree rooted at vertex 'u'.
300 // Order subtrees of 'T_u' by size.
301 ordering ord(t.get_degree(u));
302
303 // Retrieve size of every subtree. Let 'T_u[v]' be the subtree
304 // of 'T_u' rooted at vertex 'v'. Now,
305 // s[v] := the size of the subtree 'T_u[v]'
306 data_array<uint64_t> s(t.get_num_nodes());
308
309 uint64_t M = 0; // maximum of the sizes (needed for the counting sort algorithm)
310 const neighbourhood& u_neighs = t.get_neighbours(u);
311 for (std::size_t i = 0; i < u_neighs.size(); ++i) {
312 // i-th child of v_star
313 const node ui = u_neighs[i];
314 // size of subtree rooted at 'ui'
315 const uint64_t s_ui = s[ui];
316
317 ord[i].size = s_ui;
318 ord[i].v = ui;
319
320 M = std::max(M, s_ui);
321 }
324 (
325 ord.begin(), ord.end(), M, ord.size(),
326 [](const node_size& p) { return p.size; }
327 );
328
329 return ord;
330}
331
344template <char root, bool make_arrangement>
347 node one_node, position start,
348 linear_arrangement& mla, uint64_t& cost
349)
350noexcept
351{
352 std::vector<node> reachable(t.get_num_nodes_component(one_node));
353 {
354 auto it = reachable.begin();
355 detail::BFS bfs(t);
356 bfs.set_process_current([&](const auto&, node u) { *it++ = u; });
357 bfs.start_at(one_node);
358 }
359 const uint64_t size_tree = t.get_num_nodes_component(one_node);
360
361 static_assert(root == NO_ANCHOR or root == RIGHT_ANCHOR or root == LEFT_ANCHOR);
362
363#if defined DEBUG
364 assert(size_tree > 0);
365#endif
366
367 // Base case
368 if (size_tree == 1) {
369#if defined DEBUG
370 assert(one_node == reachable[0]);
371 assert(start <= t.get_num_nodes());
372#endif
373 if constexpr (make_arrangement) {
374 mla.assign(one_node, start);
375 }
376 cost = 0;
377 return;
378 }
379
380 if constexpr (root == NO_ANCHOR) {
381 const node u = detail::retrieve_centroid(t, one_node).first;
382
383 const ordering ord = get_ordering(t, u);
384
385 const auto q = calculate_q(size_tree, ord);
386 if (not q) {
387 const uint64_t n_0 = ord[0].size;
388 const node t_0 = ord[0].v;
389
390 t.remove_edge(u, t_0, false, false);
391
392 uint64_t c1 = 0;
393 calculate_mla<RIGHT_ANCHOR, make_arrangement>(t, t_0, start, mla, c1);
394
395 uint64_t c2 = 0;
396 calculate_mla<LEFT_ANCHOR, make_arrangement>(t, u, start + n_0, mla, c2);
397
398 cost = c1 + c2 + 1;
399
400 t.add_edge(u, t_0, false, false);
401 }
402 else {
403 // uq: unsigned 'q'
404 const uint64_t uq = *q;
405 cost = std::numeric_limits<uint64_t>::max();
406
407 std::vector<edge> edges(2*uq + 1);
408 for (uint64_t i = 0; i <= 2*uq; ++i) {
409 edges[i] = {u, ord[i].v};
410 }
411
412 // Transform g into Y
413 t.remove_edges(edges, false, false);
414
415 // Central tree size
416 uint64_t size_rest_of_trees = 0;
417 for (uint64_t i = 2*uq + 1; i < ord.size(); ++i) {
418 size_rest_of_trees += ord[i].size;
419 }
420
421 for (uint64_t i = 0; i <= 2*uq; ++i) {
422 const auto Q_i = get_Q(uq, i);
423
424 t.add_edge(u, ord[i].v);
425
426 uint64_t c_i = 0;
427 linear_arrangement arr_aux = mla;
428 uint64_t start_aux = start;
429
430 // Left part of the arrangement
431 for (uint64_t j = 1; j <= uq; ++j) {
432 const position pos_in_ord = Q_i[j];
433
434 uint64_t c_i_j = 0;
435 calculate_mla<RIGHT_ANCHOR, make_arrangement>(
436 t,
437 ord[pos_in_ord].v, start_aux,
438 arr_aux, c_i_j
439 );
440 start_aux += ord[pos_in_ord].size;
441 c_i += c_i_j;
442 }
443
444 // Central part of the arrangement
445 uint64_t c_i_j = 0;
446 calculate_mla<NO_ANCHOR, make_arrangement>(t, u, start_aux, arr_aux, c_i_j);
447 c_i += c_i_j;
448
449 // Right part of the arrangement
450 start_aux += ord[i].size + 1 + size_rest_of_trees;
451 for (uint64_t j = uq + 1; j <= 2*uq; ++j) {
452 const position pos_in_ord = Q_i[j];
453
454 uint64_t c_i_j_in = 0;
455 calculate_mla<LEFT_ANCHOR, make_arrangement>(
456 t,
457 ord[pos_in_ord].v, start_aux,
458 arr_aux, c_i_j_in
459 );
460 start_aux += ord[pos_in_ord].size;
461 c_i += c_i_j_in;
462 }
463
464 // Adding parts of the anchors over trees nearer to the central tree
465 c_i += size_tree*uq;
466
467 uint64_t subs = 0;
468 for (uint64_t j = 1; j <= uq; ++j) {
469 subs += (uq - j + 1)*(ord[Q_i[j]].size + ord[Q_i[2*uq - j + 1]].size);
470 }
471 c_i -= subs;
472 c_i += uq; // NOT IN CHUNG'S PAPER
473
474 if (c_i < cost) {
475 cost = c_i;
476 if constexpr (make_arrangement) {
477 mla = std::move(arr_aux);
478 }
479 }
480#if defined DEBUG
481 assert(u != ord[i].v);
482#endif
483 t.remove_edge(u, ord[i].v, false, false);
484 }
485
486 // Transform g into its previous form
487 t.add_edges(edges, false, false);
488 }
489 }
490 else { // root == ANCHOR
491 const ordering ord = get_ordering(t, one_node);
492
493 const auto p = calculate_p(size_tree, ord);
494 if (not p) {
495 const uint64_t n_0 = ord[0].size;
496 const node t_0 = ord[0].v;
497#if defined DEBUG
498 assert(one_node != t_0);
499#endif
500
501 t.remove_edge(one_node, t_0, false, false);
502
503 uint64_t c1 = 0;
504 uint64_t c2 = 0;
505
506 if constexpr (root == LEFT_ANCHOR) {
507 calculate_mla<NO_ANCHOR, make_arrangement>
508 (t, one_node, start, mla, c1);
509
510 calculate_mla<LEFT_ANCHOR, make_arrangement>
511 (t, t_0, start + size_tree - ord[0].size, mla, c2);
512 }
513 else {
514 calculate_mla<RIGHT_ANCHOR, make_arrangement>
515 (t, t_0, start, mla, c1);
516
517 calculate_mla<NO_ANCHOR, make_arrangement>
518 (t, one_node, start + n_0, mla, c2);
519 }
520
521 cost = c1 + c2 + size_tree - ord[0].size;
522 t.add_edge(one_node, t_0, false, false);
523
524 /*
525 if constexpr (root == LEFT_ANCHOR) {
526 //if (2*mla[one_node - 1] - 2*start > size_tree - 1) {
527
528 // Left anchor and the root is too much to the right
529 for (uint64_t i = 0; i < size_tree; ++i) {
530 const uint64_t aux = start + size_tree - 1 - mla[reachable[i]] + start;
531 mla[reachable[i]] = aux;
532 }
533 //}
534 }
535 */
536 }
537 else {
538 // up: unsigned 'p'
539 const uint64_t up = *p;
540 cost = std::numeric_limits<uint64_t>::max();
541
542 std::vector<edge> edges(2*up + 2);
543 for (uint64_t i = 0; i <= 2*up + 1; ++i) {
544 edges[i] = {one_node, ord[i].v};
545 }
546
547 // Transform g into Y
548 t.remove_edges(edges, false, false);
549
550 // Central tree size
551 uint64_t size_rest_of_trees= 0;
552 for (uint64_t i = 2*up + 2; i < ord.size() ;++i) {
553 size_rest_of_trees += ord[i].size;
554 }
555
556 for (uint64_t i = 0; i <= 2*up + 1; ++i) {
557 const auto P_i = get_P(up, i);
558 t.add_edge(one_node, ord[i].v, false, false);
559
560 uint64_t c_i = 0;
561 linear_arrangement arr_aux = mla;
562 uint64_t start_aux = start;
563
565 if constexpr (root == LEFT_ANCHOR) {
566 // Left part of the arrangement
567 for (uint64_t j = 1; j <= up; ++j) {
568 const position pos_in_ord = P_i[j];
569
570 uint64_t c_i_j_in = 0;
571 calculate_mla<RIGHT_ANCHOR, make_arrangement>(
572 t,
573 ord[pos_in_ord].v, start_aux,
574 arr_aux, c_i_j_in
575 );
576 start_aux += ord[pos_in_ord].size;
577 c_i += c_i_j_in;
578 }
579
580 // Central part of the arrangement
581 uint64_t c_i_j = 0;
582 calculate_mla<NO_ANCHOR, make_arrangement>
583 (t, one_node, start_aux, arr_aux, c_i_j);
584
585 start_aux += ord[i].size + 1 + size_rest_of_trees;
586 c_i += c_i_j;
587
588 // Right part of the arrangement
589 for (uint64_t j = up + 1; j <= 2*up + 1; ++j) {
590 const position pos_in_ord = P_i[j];
591
592 uint64_t c_i_j_in = 0;
593 calculate_mla<LEFT_ANCHOR, make_arrangement>(
594 t,
595 ord[pos_in_ord].v, start_aux,
596 arr_aux, c_i_j_in
597 );
598 start_aux += ord[pos_in_ord].size;
599 c_i += c_i_j_in;
600 }
601 }
602 else { // root == RIGHT_ANCHOR
603
604 // Right part of the arrangement
605 for (uint64_t j = 2*up + 1; j >= up + 1; --j) {
606 const position pos_in_ord = P_i[j];
607
608 uint64_t c_i_j_in = 0;
609 calculate_mla<RIGHT_ANCHOR, make_arrangement>(
610 t,
611 ord[pos_in_ord].v, start_aux,
612 arr_aux, c_i_j_in
613 );
614 start_aux += ord[pos_in_ord].size;
615 c_i += c_i_j_in;
616 }
617
618 // Central part of the arrangement
619 uint64_t c_i_j = 0;
620 calculate_mla<NO_ANCHOR, make_arrangement>
621 (t, one_node, start_aux, arr_aux, c_i_j);
622
623 start_aux += ord[i].size + 1 + size_rest_of_trees;
624 c_i += c_i_j;
625
626 // Left part of the arrangement
627 for (uint64_t j = up; j >= 1; --j) {
628 const position pos_in_ord = P_i[j];
629
630 uint64_t c_i_j_in = 0;
631 calculate_mla<LEFT_ANCHOR, make_arrangement>(
632 t,
633 ord[pos_in_ord].v, start_aux,
634 arr_aux, c_i_j_in
635 );
636 start_aux += ord[pos_in_ord].size;
637 c_i += c_i_j_in;
638 }
639 }
641
642 // Adding parts of the anchors over trees nearer to the central tree
643 c_i += size_tree*(up + 1);
644 c_i -= (up + 1)*ord[P_i[P_i.size()-1]].size;
645
646 uint64_t subs = 0;
647 for (uint64_t j = 1; j <= up; ++j) {
648 subs += (up - j + 1)*(ord[P_i[j]].size + ord[P_i[2*up - j + 1]].size);
649 }
650 c_i -= subs;
651 c_i += up; // NOT IN CHUNG'S PAPER
652
653 if (c_i < cost) {
654 cost = c_i;
655 mla = arr_aux;
656 }
657#if defined DEBUG
658 assert(one_node != ord[i].v);
659#endif
660 t.remove_edge(one_node, ord[i].v, false, false);
661 }
662
663 // Transform g into its previous form
664 t.add_edges(edges, false, false);
665
666 }
667 }
668}
669
670} // -- namespace dmin_Chung
671
681template <bool make_arrangement>
682std::conditional_t<
683 make_arrangement,
684 std::pair<uint64_t, linear_arrangement>,
685 uint64_t
686>
688{
689#if defined DEBUG
690 assert(t.is_tree());
691#endif
692
693 graphs::free_tree T = t;
694
695 uint64_t Dmin = 0;
696 linear_arrangement arr(make_arrangement ? t.get_num_nodes() : 0);
697 Chung::calculate_mla<Chung::NO_ANCHOR, make_arrangement>(T, 0, 0, arr, Dmin);
698
699 if constexpr (make_arrangement) {
700 return {Dmin, std::move(arr)};
701 }
702 else {
703 return Dmin;
704 }
705}
706
707} // -- namespace unconstrained
708} // -- namespace Dmin
709} // -- namespace detail
710} // -- namespace lal
Abstract graph Breadth-First Search traversal.
Definition: traversal.hpp:89
void set_process_current(const BFS_process_one &f) noexcept
Set the function that controls the processing of the current node.
Definition: traversal.hpp:186
void start_at(node source) noexcept
Start traversal at a given node.
Definition: traversal.hpp:152
Free tree graph class.
Definition: free_tree.hpp:60
Linear arrangement of vertices.
Definition: linear_arrangement.hpp:103
ordering get_ordering(const graphs::free_tree &t, node u) noexcept
Sort the children of u in the rooted tree .
Definition: Dmin_Unconstrained_FC.hpp:298
std::optional< uint64_t > calculate_q(uint64_t n, const ordering &ord) noexcept
Calculate .
Definition: Dmin_Unconstrained_FC.hpp:116
void calculate_mla(graphs::free_tree &t, node one_node, position start, linear_arrangement &mla, uint64_t &cost) noexcept
Calculate a minimum linear arrangment using Fan Chung's algorithm.
Definition: Dmin_Unconstrained_FC.hpp:345
data_array< uint64_t > get_P(uint64_t p, uint64_t i) noexcept
Calculate .
Definition: Dmin_Unconstrained_FC.hpp:227
std::optional< uint64_t > calculate_p(uint64_t n, const ordering &ord) noexcept
Calculate .
Definition: Dmin_Unconstrained_FC.hpp:190
data_array< uint64_t > get_Q(uint64_t q, uint64_t i) noexcept
Calculate .
Definition: Dmin_Unconstrained_FC.hpp:263
lal::detail::data_array< node_size > ordering
Typedef for a useful type.
Definition: Dmin_Unconstrained_FC.hpp:75
std::conditional_t< make_arrangement, std::pair< uint64_t, linear_arrangement >, uint64_t > FanChung_2(const graphs::free_tree &t) noexcept
Calculates a minimum linear arrangment using Fan Chung's algorithm.
Definition: Dmin_Unconstrained_FC.hpp:687
constexpr char NO_ANCHOR
The tree is not anchored.
Definition: Dopt_utils.hpp:92
constexpr char RIGHT_ANCHOR
The tree is right-anchored.
Definition: Dopt_utils.hpp:90
constexpr char LEFT_ANCHOR
The tree is left-anchored.
Definition: Dopt_utils.hpp:88
void counting_sort(const value_iterator_t begin, const value_iterator_t end, const std::size_t largest_key_plus_1, const std::function< std::size_t(const value_t &)> &key, countingsort::memory< value_t > &mem) noexcept
Counting sort algorithm with reusable memory.
Definition: counting_sort.hpp:155
void get_size_subtrees(const tree_t &t, const node u, const node v, uint64_t *const sizes) noexcept
Calculate the size of every subtree of the tree t.
Definition: size_subtrees.hpp:74
std::pair< node, node > retrieve_centroid(const tree_t &t, const node x) noexcept
Calculate the centroid of the connected component that has node x.
Definition: tree_centroid.hpp:396
Main namespace of the library.
Definition: basic_types.hpp:50
uint64_t position
Node's position type.
Definition: basic_types.hpp:55
uint64_t node
Node type. See Node / Vertex page for further details.
Definition: basic_types.hpp:53
std::vector< node > neighbourhood
List of nodes.
Definition: basic_types.hpp:62
Wrapper of a C array for autmatic deallocation of memory.
Definition: data_array.hpp:59
std::size_t size() const noexcept
Size of the array.
Definition: data_array.hpp:204
T * end() noexcept
Non-constant raw pointer to last+1 element.
Definition: data_array.hpp:293
T * begin() noexcept
Non-constant raw pointer to first element.
Definition: data_array.hpp:291
Struct used in many algorithms to sort vertices according to some integer value.
Definition: pairs_utils.hpp:54
Non-increasing sort.
Definition: sorting_types.hpp:51