BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlb_topologicalsortutil.h
Go to the documentation of this file.
1/// @file bdlb_topologicalsortutil.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlb_topologicalsortutil.h -*-C++-*-
8#ifndef INCLUDED_BDLB_TOPOLOGICALSORTUTIL
9#define INCLUDED_BDLB_TOPOLOGICALSORTUTIL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlb_topologicalsortutil bdlb_topologicalsortutil
15/// @brief Provide a utility to topologically sort a collection of inputs.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlb
19/// @{
20/// @addtogroup bdlb_topologicalsortutil
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlb_topologicalsortutil-purpose"> Purpose</a>
25/// * <a href="#bdlb_topologicalsortutil-classes"> Classes </a>
26/// * <a href="#bdlb_topologicalsortutil-description"> Description </a>
27/// * <a href="#bdlb_topologicalsortutil-self-referencing-nodes"> Self-referencing Nodes </a>
28/// * <a href="#bdlb_topologicalsortutil-concepts"> Concepts </a>
29/// * <a href="#bdlb_topologicalsortutil-node_type-concept"> NODE_TYPE Concept </a>
30/// * <a href="#bdlb_topologicalsortutil-edgetype-concept"> EdgeType Concept </a>
31/// * <a href="#bdlb_topologicalsortutil-input_iter-concept"> INPUT_ITER Concept </a>
32/// * <a href="#bdlb_topologicalsortutil-output_iter-concept"> OUTPUT_ITER Concept </a>
33/// * <a href="#bdlb_topologicalsortutil-unordered_iter-concept"> UNORDERED_ITER Concept </a>
34/// * <a href="#bdlb_topologicalsortutil-usage"> USAGE: </a>
35/// * <a href="#bdlb_topologicalsortutil-example-1-using-topological-sort-for-calculating-formulas"> Example 1: Using Topological Sort for Calculating Formulas </a>
36/// * <a href="#bdlb_topologicalsortutil-example-2-using-topological-sort-with-cycles-in-input"> Example 2: Using Topological Sort with Cycles in Input </a>
37/// * <a href="#bdlb_topologicalsortutil-example-3-using-topological-sort-with-self-relations"> Example 3: Using Topological Sort with Self Relations </a>
38/// * <a href="#bdlb_topologicalsortutil-example-4-using-topological-sort-with-iterators-as-input"> Example 4: Using Topological Sort with Iterators as Input </a>
39/// * <a href="#bdlb_topologicalsortutil-example-5-using-topological-sort-with-iterators-as-output"> Example 5: Using Topological Sort with Iterators as Output </a>
40///
41/// # Purpose {#bdlb_topologicalsortutil-purpose}
42/// Provide a utility to topologically sort a collection of inputs.
43///
44/// # Classes {#bdlb_topologicalsortutil-classes}
45///
46/// - bdlb::TopologicalSortUtil: utility for topologically sorting inputs
47/// - bdlb::TopologicalSortUtilEdgeTraits: customization point for edge types
48///
49/// @see
50///
51/// # Description {#bdlb_topologicalsortutil-description}
52/// This component provides a utility `struct`,
53/// `bdlb::TopologicalSortUtil`, to topologically sort a collection of inputs
54/// that describe a directed acyclic graph (also known as DAG). A topological
55/// sort is useful for defining the order in which an input should be processed
56/// based on certain conditions. As an example, consider two jobs B and C both
57/// of which have a dependency on job A, i.e., job A needs to be completed
58/// before B or C can be run. Given such a requirement, a topological sort can
59/// provide an ordering of the three jobs such that A precedes B and C. Note
60/// that there may be multiple orderings that might be correct (e.g.: A, B, then
61/// C or A, C then B; both are correct and satisfy the dependencies of A running
62/// before the B and C jobs; we don't care whether B precedes C or vice versa).
63///
64/// The dependencies are specified in pairs (U,V) (read as U precedes V), which
65/// define the relations between U and V that the sort needs to maintain. The
66/// elements in these pairs (e.g., U and V) make up a finite set S. The output
67/// of the topological sort is an ordering of all elements of set S, such that
68/// all relations specified in the input (as pairs) are satisfied. Note that
69/// such an ordering is only possible only if there are no cycles in the input
70/// dependencies. For example, if A depends on B and B depends on A, then it is
71/// not possible to order A and B satisfying both the relations. The routine
72/// `sort` defined in this component returns `false` if it detects a cycle while
73/// trying to sort the input.
74///
75/// Complexity of the topological sort in this component is `O(n+m)` where n is
76/// the number of input elements in the finite set S and m is the number of
77/// relations as specified by the input pairs.
78///
79/// The `sort` function is provided in two variants or flavors: simple and
80/// iterator-based. The simple flavor is templated on the `NODE_TYPE`, and it
81/// provides topological sorting for the case where the input is a `bsl::vector`
82/// of `bsl::pair`s, and the outputs are `bsl::vector`s. The templated variant
83/// is highly customizable (templated) on both the input and the outputs. The
84/// input may be any input iterator range that has a `bsl::pair` `value_type` or
85/// a `value_type` with a `bdlb::TopologicalSortUtilEdgeTraits` specialization.
86/// The `value_type` is determined using `bsl::iterator::traits`. The two
87/// outputs are both defined as templated output iterators and they may have
88/// different types. So (for example) the iterator based `sort` may be used
89/// with Null `OUTPUT_ITER` to answer the question "does this graph have
90/// cycles?" while not wasting memory in storing the sort results.
91///
92/// ## Self-referencing Nodes {#bdlb_topologicalsortutil-self-referencing-nodes}
93///
94///
95/// Some graph representations may use self-referencing nodes to indicate nodes
96/// without any connection -- where a self referencing node in the context of
97/// this component is a input edge pair like (u, u). The implementation of
98/// `sort` in this component treats such input entries as cycles and fails
99/// sorting.
100///
101/// You may still use this `sort` implementation (to process your nodes in
102/// proper order) if your data structure contains such entries. To process a
103/// graph having self-referencing nodes one may create a filtering iterator on
104/// top of the input that removes any self-referential edges from the input.
105/// Any filtered nodes could be treated as being sorted first.
106///
107/// ## Concepts {#bdlb_topologicalsortutil-concepts}
108///
109///
110/// This component provides generic (templated) utilities. This section
111/// describes the requirements of the type parameters (concepts) of these
112/// generic methods.
113///
114/// ### NODE_TYPE Concept {#bdlb_topologicalsortutil-node_type-concept}
115///
116///
117/// The input for a topological sort is supplied as pairs (though not
118/// necessarily `bsl::pair`s) of `NODE_TYPE` values. `NODE_TYPE` is aliased to
119/// `NodeType` in `TopologicalSortUtilEdgeTraits`.
120///
121/// `NODE_TYPE` shall be a value type that supports hashing using the
122/// `bsl::hash<NODE_TYPE>` functor type and equality comparison using the
123/// `bsl::equal_to<NODE_TYPE>` functor type.
124///
125/// Notice that we have not provided a customization point for the hash functor
126/// type nor for the equality functor type because that would complicate the
127/// code greatly (esp. readability would suffer).
128///
129/// ### EdgeType Concept {#bdlb_topologicalsortutil-edgetype-concept}
130///
131///
132/// The `EdgeType` describes an edge as a (conceptual) pair of `NODE_TYPE`
133/// values and is supplied as the input to the topological sort methods.
134/// `EdgeType` is the `value_type` of the `INPUT_ITER` supplied to the `sort`
135/// functions. Conceptually it represents a pair (U,V) (where U and V are
136/// `NodeType`s) that means "U precedes V". The `EdgeType` supplied to a sort
137/// is typically a `bsl::pair`, but users may customize this by specializing the
138/// `TopologicalSortUtilEdgeTraits` type. `TopologicalSortUtil` determines the
139/// `NodeType` and access operations on `EdgeType` via the
140/// `TopologicalSortUtilEdgeTraits` type.
141///
142/// `TopologicalSortUtilEdgeTraits` is specialized for `bsl::pair<T,T>`, the
143/// user needs to (partially or fully) specialize it for other types. See the
144/// `CustomEdge` specialization in the test driver for an example.
145///
146/// ### INPUT_ITER Concept {#bdlb_topologicalsortutil-input_iter-concept}
147///
148///
149/// `INPUT_ITER` is an input iterator type used by the `sort` functions, and its
150/// `value_type` is `EdgeType` .
151///
152/// ### OUTPUT_ITER Concept {#bdlb_topologicalsortutil-output_iter-concept}
153///
154///
155/// `OUTPUT_ITER` is an output iterator for `sort` functions, and its
156/// `value_type` is `NodeType`.
157///
158/// ### UNORDERED_ITER Concept {#bdlb_topologicalsortutil-unordered_iter-concept}
159///
160///
161///
162/// `UNORDERED_ITER` is a (second) output iterator for `sort` functions, and its
163/// `value_type` is `NodeType`. Note that `OUTPUT_ITERATOR` and
164/// `UNORDERED_ITER` are distinct types allowing the sorted output nodes to be
165/// collected in a different way from the unsorted output nodes(e.g., into a
166/// different type of container).
167///
168/// ## USAGE: {#bdlb_topologicalsortutil-usage}
169///
170///
171/// This section illustrates intended use of this component.
172///
173/// ### Example 1: Using Topological Sort for Calculating Formulas {#bdlb_topologicalsortutil-example-1-using-topological-sort-for-calculating-formulas}
174///
175///
176/// Suppose we are evaluating formulas for a set of market data fields, where
177/// formulas can reference other fields as part of their calculation. As an
178/// example, let's say we have a field `k_bbgDefinedVwap` that is dependent on
179/// `k_vwapTurnover` and `k_vwapVolume`. These fields in turn are dependent on
180/// `k_tradeSize` and `k_tradePrice` respectively. So essentially the fields
181/// that are not dependent on any other field should be calculated first and
182/// then the dependent fields. We can use the topological sort utility to
183/// provide us the order in which these fields should be calculated.
184///
185/// First, we create the relations showcasing the above mentioned dependencies
186/// in the form of pairs (a,b) where b is dependent on a:
187/// @code
188/// enum FieldIds {
189/// k_bbgDefinedVwap = 0,
190/// k_vwapTurnover = 1,
191/// k_vwapVolume = 2,
192/// k_tradeSize = 3,
193/// k_tradePrice = 4
194/// };
195///
196/// bsl::vector<bsl::pair<int, int> > relations;
197///
198/// relations.push_back(bsl::make_pair(static_cast<int>(k_vwapTurnover),
199/// static_cast<int>(k_bbgDefinedVwap)));
200/// relations.push_back(bsl::make_pair(static_cast<int>(k_vwapVolume),
201/// static_cast<int>(k_bbgDefinedVwap)));
202/// relations.push_back(bsl::make_pair(static_cast<int>(k_tradeSize),
203/// static_cast<int>(k_vwapVolume)));
204/// relations.push_back(bsl::make_pair(static_cast<int>(k_tradeSize),
205/// static_cast<int>(k_vwapTurnover)));
206/// relations.push_back(bsl::make_pair(static_cast<int>(k_tradePrice),
207/// static_cast<int>(k_vwapTurnover)));
208/// @endcode
209/// Now, we call the topological sort to get a topological order for the fields
210/// referenced in the relations:
211/// @code
212/// bsl::vector<int> results;
213/// bsl::vector<int> unsorted;
214/// bool sorted = TopologicalSortUtil::sort(&results,
215/// &unsorted,
216/// relations);
217/// @endcode
218/// Finally, we verify that the call to `sort` populates the supplied `results`
219/// with a sequence in sorted order (e.g.. `k_tradeSize`, 'k_tradePrice,
220/// `k_vwapTurnover`, `k_vwapVolume`, `k_bbgDefinedVwap`) and `unsorted` will be
221/// empty because the input relationships do not contain a cycle.
222/// @code
223/// bool calculated[5] = { 0 };
224/// assert(sorted == true);
225/// assert(unsorted.empty());
226///
227/// for (bsl::vector<int>::const_iterator iter = results.begin(),
228/// end = results.end();
229/// iter != end; ++iter) {
230/// switch (*iter) {
231/// case k_bbgDefinedVwap: {
232/// assert(calculated[k_vwapTurnover] == true);
233/// assert(calculated[k_vwapVolume] == true);
234///
235/// assert(calculated[k_bbgDefinedVwap] == false);
236/// calculated[k_bbgDefinedVwap] = true;
237/// } break;
238/// case k_vwapTurnover: {
239/// assert(calculated[k_tradeSize] == true);
240/// assert(calculated[k_tradePrice] == true);
241///
242/// assert(calculated[k_vwapTurnover] == false);
243/// calculated[k_vwapTurnover] = true;
244/// } break;
245/// case k_vwapVolume: {
246/// assert(calculated[k_tradeSize] == true);
247///
248/// assert(calculated[k_vwapVolume] == false);
249/// calculated[k_vwapVolume] = true;
250/// } break;
251/// case k_tradeSize: {
252/// assert(calculated[k_vwapVolume] == false);
253/// assert(calculated[k_vwapTurnover] == false);
254///
255/// assert(calculated[k_tradeSize] == false);
256/// calculated[k_tradeSize] = true;
257/// } break;
258/// case k_tradePrice: {
259/// assert(calculated[k_vwapTurnover] == false);
260///
261/// assert(calculated[k_tradePrice] == false);
262/// calculated[k_tradePrice] = true;
263/// } break;
264/// default:
265/// assert(false);
266/// break;
267/// };
268/// }
269///
270/// for (int i = 0; i < 5; ++i) {
271/// assert(calculated[i] == true);
272/// }
273/// @endcode
274/// ### Example 2: Using Topological Sort with Cycles in Input {#bdlb_topologicalsortutil-example-2-using-topological-sort-with-cycles-in-input}
275///
276///
277/// Suppose we have a set of inputs that contain a cycle.
278///
279/// First, we define a set of inputs that have a cycle:
280/// @code
281/// enum FieldIds2 {
282/// k_FIELD1 = 0,
283/// k_FIELD2 = 1,
284/// k_FIELD3 = 2
285/// };
286///
287/// bsl::vector<bsl::pair<int, int> > relations2;
288///
289/// relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD2),
290/// static_cast<int>(k_FIELD1)));
291/// relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD3),
292/// static_cast<int>(k_FIELD2)));
293/// relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD1),
294/// static_cast<int>(k_FIELD3)));
295/// @endcode
296/// Now, we apply the topological sort routine on the input:
297/// @code
298/// bsl::vector<int> results2;
299/// bsl::vector<int> unordered2;
300/// bool sorted2 = TopologicalSortUtil::sort(&results2,
301/// &unordered2,
302/// relations2);
303/// @endcode
304/// Finally, we verify whether the routine recognizes that there is a cycle and
305/// returns false, and the 3 nodes comprising the cycle are reported in
306/// `unordered2`:
307/// @code
308/// assert(sorted2 == false);
309/// assert(unordered2.size() == 3);
310/// @endcode
311/// ### Example 3: Using Topological Sort with Self Relations {#bdlb_topologicalsortutil-example-3-using-topological-sort-with-self-relations}
312///
313///
314/// Suppose we have a set of inputs that have input relations where predecessor
315/// and successor point to the same value, i.e. we have pairs of input like
316/// (u,u). This example demonstrates that the `sort` function handles such
317/// input as a cycle. (See Example 6 for a way of processing such nodes and
318/// avoiding `sort` reporting a cycle.) First, we define such a set of inputs:
319/// @code
320/// enum FieldIds3 {
321/// k_FIELD4 = 3,
322/// k_FIELD5 = 4,
323/// k_FIELD6 = 5
324/// };
325///
326/// bsl::vector<bsl::pair<int, int> > relations3;
327///
328/// relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD4),
329/// static_cast<int>(k_FIELD6)));
330/// relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD5),
331/// static_cast<int>(k_FIELD4)));
332/// relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD4),
333/// static_cast<int>(k_FIELD4)));
334/// @endcode
335/// Now, we apply the topological sort routine on the input:
336/// @code
337/// bsl::vector<int> results3;
338/// bsl::vector<int> unordered3;
339/// bool sorted3 = TopologicalSortUtil::sort(&results3,
340/// &unordered3,
341/// relations3);
342/// @endcode
343/// Finally, we verify that the self relations causes the cycle:
344/// @code
345/// assert(sorted3 == false);
346/// assert(results3.size() == 1);
347/// assert(unordered3.size() == 2);
348///
349/// if (veryVeryVerbose) {
350/// cout << "Size of results3 vector is "
351/// << results3.size() << endl;
352///
353/// cout << "Size of unordered3 vector is "
354/// << unordered3.size() << endl;
355/// }
356///
357/// if (veryVeryVerbose)
358/// {
359/// for (bsl::size_t i = 0; i < results3.size(); ++i) {
360/// cout << "results3[" << i << "] is " << results3[i] << "\n";
361/// }
362///
363/// for (bsl::size_t i = 0; i < unordered3.size(); ++i) {
364/// cout << "unordered3[" << i << "] is " << unordered3[i] << "\n";
365/// }
366/// }
367/// @endcode
368/// ### Example 4: Using Topological Sort with Iterators as Input {#bdlb_topologicalsortutil-example-4-using-topological-sort-with-iterators-as-input}
369///
370///
371/// Suppose we have a set of inputs that have input relations that conceptually
372/// follow the input requirements (of a listing of pairs of nodes) but are not
373/// physically stored in a `bsl::vector` of `bsl::pair` typed container. Let's
374/// suppose the input is in a `bsl::list` instead. First, we define such a set
375/// of inputs:
376/// @code
377/// bsl::list<bsl::pair<int, int> > relations4;
378///
379/// relations4.push_back(bsl::make_pair(1, 2));
380/// relations4.push_back(bsl::make_pair(1, 3));
381/// relations4.push_back(bsl::make_pair(2, 3));
382/// @endcode
383/// Now, we apply the topological sort routine on the input:
384/// @code
385/// bsl::vector<int> results4;
386/// bsl::vector<int> unordered4;
387/// typedef bsl::back_insert_iterator<bsl::vector<int> > OutIter;
388/// bool sorted4 = TopologicalSortUtil::sort(relations4.begin(),
389/// relations4.end(),
390/// OutIter(results4),
391/// OutIter(unordered3));
392/// @endcode
393/// Finally, we verify that the sort is successful, there are no nodes in the
394/// `unsorted` output (there is no cycle) and the nodes are listed in the
395/// proper order in `results4`:
396/// @code
397/// assert(sorted4 == true);
398/// assert(unordered4.size() == 0);
399/// assert(results4.size() == 3);
400///
401/// assert(results4[0] == 1);
402/// assert(results4[1] == 2);
403/// assert(results4[2] == 3);
404/// @endcode
405/// ### Example 5: Using Topological Sort with Iterators as Output {#bdlb_topologicalsortutil-example-5-using-topological-sort-with-iterators-as-output}
406///
407///
408/// Suppose we want our result in a `bsl::list` instead of a `bsl::vector` and
409/// we do not care about the unsorted elements so we do not want to pay for
410/// storing them if they exist. First, we would define a Null Output Iterator
411/// that writes to nowhere:
412/// @code
413/// class NullOutputIterator {
414/// public:
415/// typedef void container_type;
416/// typedef void value_type;
417/// typedef void difference_type;
418/// typedef void pointer;
419/// typedef void reference;
420/// typedef bsl::output_iterator_tag iterator_category;
421///
422/// template <class T>
423/// NullOutputIterator& operator=(const T&)
424/// {
425/// return *this;
426/// }
427///
428/// NullOutputIterator& operator*()
429/// {
430/// return *this;
431/// }
432///
433/// NullOutputIterator& operator++()
434/// {
435/// return *this;
436/// }
437///
438/// NullOutputIterator& operator++(int)
439/// {
440/// return *this;
441/// }
442/// };
443/// @endcode
444/// Now, we apply the topological sort routine on the input:
445/// @code
446/// bsl::list<int> results5;
447/// typedef bsl::back_insert_iterator<bsl::list<int> > ListOutIter;
448/// bool sorted5 = TopologicalSortUtil::sort(relations4.begin(),
449/// relations4.end(),
450/// ListOutIter(results5),
451/// NullOutputIterator());
452/// @endcode
453/// Finally, we verify that the sort is successful, and the 3 nodes are listed
454/// in the proper order in the `results5` list:
455/// @code
456/// assert(sorted5 == true);
457/// assert(results5.size() == 3);
458///
459/// assert(results5[0] == 1);
460/// assert(results5[1] == 2);
461/// assert(results5[2] == 3);
462/// @endcode
463/// @}
464/** @} */
465/** @} */
466
467/** @addtogroup bdl
468 * @{
469 */
470/** @addtogroup bdlb
471 * @{
472 */
473/** @addtogroup bdlb_topologicalsortutil
474 * @{
475 */
476
477#include <bdlscm_version.h>
478
479#include <bslma_allocator.h>
481
485
486#include <bsls_assert.h>
487
488#include <bsl_iterator.h>
489#include <bsl_queue.h>
490#include <bsl_unordered_map.h>
491#include <bsl_utility.h>
492#include <bsl_vector.h>
493
494#include <utility> // for 'std::pair'
495
496
497namespace bdlb {
498
499 // ===================================
500 // class TopologicalSortUtilEdgeTraits
501 // ===================================
502
503/// This `struct` represents a customization point allowing clients to
504/// supply input iterators to `sort` having a @ref value_types other than a
505/// `bsl::pair`. Clients may specialize `TopologicalSortUtilEdgeTraits` to
506/// supply the following:
507/// @code
508/// /// The type of the directed connection from one node to another
509/// /// `bsl::pair<NodeType, NodeType>` and `std::pair<NodeType>` work
510/// /// without `TopologicalSortUtilEdgeTraits` specialization.
511/// typedef EDGE_TYPE EdgeType;
512///
513/// /// Alias describing the output values from a sort, as well as the
514/// /// results of the `from` and `to` functions of this edge traits
515/// /// instance. Or in other words, the node (or node identifier) type
516/// /// of the directed acyclic graph.
517/// typedef user-defined NodeType;
518///
519/// /// Return a `const` reference to the "from" attribute of the
520/// /// specified `input`. Note that the template parameter type
521/// /// `EDGE_TYPE` is an element in the input range to `sort`.
522/// static const NodeType& from(const EDGE_TYPE& input);
523///
524/// /// Return a `const` reference to the "from" attribute of the
525/// /// specified `input`. Note that the template parameter type
526/// /// `EDGE_TYPE` is an element in the input range to `sort`.
527/// static const NodeType& to(const EDGE_TYPE& input)
528/// @endcode
529template <class EDGE_TYPE>
532
533/// This `struct` is a specialization (customization) of
534/// `TopologicalSortUtilEdgeTraits` for bsl::pair<T, T>.
535template <class NODE_TYPE>
536struct TopologicalSortUtilEdgeTraits<bsl::pair<NODE_TYPE, NODE_TYPE> > {
537
538 // TYPES
539
540 /// The type of the directed connection from one node to another
542
543 /// The type of values of the nodes of the directed acyclic graph
544 typedef NODE_TYPE NodeType;
545
546 // CLASS METHODS
547
548 /// Return a `const` reference to the `from` attribute of the specified
549 /// `edge` object.
550 static const NODE_TYPE& from(const EdgeType& edge);
551
552 /// Return a `to` reference to the `from` attribute of the specified
553 /// `edge` object.
554 static const NODE_TYPE& to(const EdgeType& edge);
555};
556
557 // ================================
558 // class TopologicalSortUtil_Helper
559 // ================================
560
561/// This class template provides data structures required to sort the
562/// partial unsorted edges into a total ordered set of nodes. The value
563/// type of the nodes(*) must be hashable and equality comparable by
564/// `bsl::hash`, and `bsl::equal_to` respectively.
565///
566/// * (*) The value type is determined by first getting the iterator's value
567/// type using `bsl::iterator_traits<INPUT_ITER>::ValueType` and then
568/// using `TopologicalSortUtilEdgeTraits::ValueType` on that result.
569///
570/// See @ref bdlb_topologicalsortutil
571template <class INPUT_ITER>
573
574 private:
575 // PRIVATE TYPES
576 typedef bsl::iterator_traits<INPUT_ITER> InIterTraits;
577 typedef typename InIterTraits::value_type EdgeType;
579
580 /// Get the traits type that corresponds to the incoming edge type, then
581 /// the types from it, with short names.
582 typedef typename EdgeTraits::NodeType NodeType;
583
585 typedef typename List::iterator ListIter;
586
587 /// An ordered list of nodes and their iterators
588 typedef typename List::const_iterator ListConstIter;
589
590 /// Relations information for a given node in the format necessary for
591 /// topological sorting. The attributes are:
592 ///
593 /// * **the predecessor count**:
594 /// How many need to be sorted before this node comes in order?
595 ///
596 /// * **the successor nodes**:
597 /// All the nodes that must come after this node in the order
598 struct Links {
599
600 // PUBLIC DATA
601 int d_predecessorCount; // How many are before us?
602 List d_successors; // those that are after us
603
604 // TRAITS
606
607 // CREATORS
608
609 /// Create a `Links` which holds empty predecessor and successor
610 /// information. Optionally, specify an `allocator` for needed
611 /// memory. If `allocator` is 0, use the globally supply default
612 /// allocator instead.
613 explicit Links(bslma::Allocator *allocator = 0);
614
615 /// Create a `Links` object having the same attribute values as the
616 /// specified `original` object. Optionally specify an `allocator`
617 /// used to supply memory. If `allocator` is 0, the currently
618 /// installed default allocator is used.
619 Links(const Links& original, bslma::Allocator *allocator = 0);
620 };
621
623 typedef typename LinksMap::iterator LinksMapIter;
624
625 /// Mapping of nodes to their collected relations-information
626 typedef typename LinksMap::const_iterator LinksMapConstIter;
627
628 /// FIFO queue of nodes to process. Note that it is safe to store
629 /// iterators into the work-set/mapping in the queue because
630 /// `bsl::unordered_map` guarantees the stability of every iterator
631 /// after a delete, except the iterators pointing to the deleted entry
632 /// itself.
634
635 private:
636 // DATA
637 LinksMap d_workSet; // mapping from nodes to their relations
638 // information (predecessorCount, successors)
639
640 Fifo d_readyNodes; // elements whose predecessors have all already
641 // been sorted (or never had predecessors) as
642 // iterators into the mapping (`d_workSet`)
643
644 private:
645 // NOT IMPLEMENTED
648
649 public:
650 // TRAITS
653
654 // CREATORS
655
656 /// Create a helper class that holds the different data structures
657 /// required to sort in topological order the directed acyclic graph
658 /// described by the specified `relationsBegin` and `relationsEnd`.
659 /// Optionally, specify an `allocator` for needed memory. If
660 /// `allocator` is 0, use the globally supply default allocator instead.
661 explicit TopologicalSortUtil_Helper(INPUT_ITER relationsBegin,
662 INPUT_ITER relationsEnd,
663 bslma::Allocator *allocator = 0);
664
665 // MANIPULATORS
666
667 /// Write the next element in order that doesn't have any predecessors
668 /// into the specified `resultOutIter_p` output iterator then increment
669 /// it and return `true`. If there are still elements left but all of
670 /// them still have predecessors do nothing and return `false`. The
671 /// behavior is undefined unless `!d_workSet.empty()`.
672 template <class OUTPUT_ITER>
673 bool processNextNodeInOrder(OUTPUT_ITER *resultOutIter_p);
674
675 /// Sort the input elements provided during construction in topological
676 /// order and write the resulting linear ordered set into the specified
677 /// `resultOutIter` and return `true`. If the sort is unsuccessful (the
678 /// input is not an acyclic directed graph) write the nodes that have
679 /// not been sorted to the specified `unsortedOutIter` output and return
680 /// `false`. See `TopologicalSortUtil::sort` "iterators overload" for
681 /// more detailed specification.
682 template <class OUTPUT_ITER, class UNSORTED_OUT_ITER>
683 bool sortImpl(OUTPUT_ITER resultOutIter,
684 UNSORTED_OUT_ITER unsortedOutIter);
685};
686
687 // =====================
688 // class TopologicalSort
689 // =====================
690
691/// This `struct` `TopologicalSortUtil` provides a namespace for topological
692/// sorting functions.
694
695 public:
696 // CLASS METHODS
697
698 /// Sort the input elements in topological order and write the resulting
699 /// linear ordered set into the specified `resultOutIter`. If the
700 /// sort is unsuccessful (the input is not an acyclic directed graph)
701 /// write the elements that have not been sorted to the specified
702 /// `unsortedOutIter` output. The input elements are provided as a
703 /// sequence of (conceptual or physical) pairs between the specified
704 /// `relationsBegin` and `relationsEnd` of the form (U, V), where U and
705 /// V are nodes, and U precedes V in the output. Return `true` on
706 /// success, and `false` if the sort fails due to a cycle in the input.
707 /// The type `bsl::iterator_traits<INPUT_ITER>::value_type` must either
708 /// be `bsl` or `std::pair` where the @ref first_type and @ref second_type are
709 /// the same as `bsl::iterator_traits<OUTPUT_ITER>::value_type` or
710 /// `TopologicalSortUtilEdgeTraits` must be specialized for the type,
711 /// i.e., the supplied `bsl::iterator_traits<INPUT_ITER>::value_type`
712 /// must support the following syntax:
713 /// @code
714 /// typedef typename bsl::iterator_traits<INPUT_ITER>::value_type
715 /// IterValue;
716 /// typedef typename bsl::iterator_traits<RESULT_ITER>::value_type
717 /// ResultValue;
718 ///
719 /// typedef typename TopologicalSortUtilEdgeTraits<IterValue> Traits;
720 ///
721 /// typedef typename Traits::NodeType NodeType;
722 ///
723 /// for (INPUT_ITER it = relationshipPairsBegin;
724 /// it != relationshipPairsEnd;
725 /// ++it) {
726 ///
727 /// *result++ = Traits::from(*it); // U
728 /// *result++ = Traits::to(*it); // V
729 /// }
730 /// @endcode
731 /// Note that when the method returns `false`, `resultOutIter` may
732 /// contain a subset of the elements in the right topological order,
733 /// essentially the elements that the routine was able to sort before
734 /// the cycle was discovered. In that case, the elements that the
735 /// routine was unable to sort were written to the `unsortedOutIter`
736 /// output iterator, in an unspecified order.
737 template <class INPUT_ITER,
738 class OUTPUT_ITER,
739 class UNSORTED_OUTPUT_ITER>
740 static bool sort(INPUT_ITER relationsBegin,
741 INPUT_ITER relationsEnd,
742 OUTPUT_ITER resultOutIter,
743 UNSORTED_OUTPUT_ITER unsortedOutIter);
744
745 /// Sort the elements of `NODE_TYPE` in topological order determined by
746 /// the specified `relations` and load the resulting linear ordered set
747 /// to the specified `result`. If the sort is unsuccessful, load the
748 /// elements that have not been ordered to the specified `unsorted`
749 /// list. The input relations are provided as pairs of the form (U, V)
750 /// where U precedes V in the output. Return `false` if sort fails due
751 /// to a cycle in the input else return `true` if sort successful. Note
752 /// that even if the method returns `false`, `result` may contain a
753 /// subset of elements in the right topological order, essentially
754 /// elements that the routine was able to sort before the cycle was
755 /// discovered and `unsorted` will contain the elements that the
756 /// routine was unable to sort.
757 template <class NODE_TYPE>
758 static bool sort(
760 bsl::vector<NODE_TYPE> *unsorted,
762};
763
764// ============================================================================
765// INLINE AND TEMPLATE FUNCTION DEFINITIONS
766// ============================================================================
767
768 // -----------------------------------
769 // class TopologicalSortUtilEdgeTraits
770 // -----------------------------------
771
772template <class NODE_TYPE>
773inline
774const NODE_TYPE&
776from(const EdgeType& edge)
777{
778 return edge.first;
779}
780
781template <class NODE_TYPE>
782inline
783const NODE_TYPE&
785to(const EdgeType& edge)
786{
787 return edge.second;
788}
789
790
791 // ------------------------------------------
792 // class TopologicalSortUtil_Helper::NodeInfo
793 // ------------------------------------------
794
795// CREATORS
796template <class INPUT_ITER>
797inline
799 bslma::Allocator *allocator)
800: d_predecessorCount(0)
801, d_successors(allocator)
802{
803}
804
805template <class INPUT_ITER>
806inline
807TopologicalSortUtil_Helper<INPUT_ITER>::Links::Links(
808 const Links& original,
809 bslma::Allocator *allocator)
810: d_predecessorCount(original.d_predecessorCount)
811, d_successors(original.d_successors, allocator)
812{
813}
814 // --------------------------------
815 // class TopologicalSortUtil_Helper
816 // --------------------------------
817
818// CREATORS
819template <class INPUT_ITER>
821 INPUT_ITER relationsBegin,
822 INPUT_ITER relationsEnd,
823 bslma::Allocator *allocator)
824: d_workSet(allocator)
825, d_readyNodes(allocator)
826{
827 // Iterate through the input list of edges and iteratively fill in the
828 // relations information needed for sorting.
829
830 for (INPUT_ITER iter = relationsBegin; iter != relationsEnd; ++iter) {
831 const NodeType& from = EdgeTraits::from(*iter);
832 const NodeType& to = EdgeTraits::to(*iter);
833 ++d_workSet[to].d_predecessorCount;
834 d_workSet[from].d_successors.push_back(to);
835 }
836
837 // Now iterate through the set and find nodes that are roots, i.e. which
838 // don't have any predecessors and hence are the first in order. We put
839 // these nodes into a queue that is ready to be written to the result.
840
841 const LinksMapIter end = d_workSet.end();
842 for (LinksMapIter iter = d_workSet.begin(); iter != end; ++iter) {
843 if (0 == iter->second.d_predecessorCount) {
844 d_readyNodes.push(iter);
845 }
846 }
847}
848
849// MANIPULATORS
850template <class INPUT_ITER>
851template <class OUTPUT_ITER>
853 OUTPUT_ITER *resultOutIter_p)
854{
855 if (d_readyNodes.empty()) {
856 // If the queue is empty but the input set is not then we have at least
857 // one cycle. We know *here* that the input set is not empty because
858 // it is a precondition for calling this function.
859 return false; // RETURN
860 }
861
862 // process the next element (in 'd_readyNodes' FIFO) with no predecessors
863 const LinksMapIter mapIter = d_readyNodes.front();
864
865 // move the processed node to the sorted output
866 *(*resultOutIter_p) = mapIter->first;
867 ++(*resultOutIter_p); // output iterator must be moved after a write
868
869 // Iterate through the successor list of the node and reduce predecessor
870 // count of each successor. Further, if that count becomes zero add that
871 // successor to 'd_readyNodes'.
872 for (ListIter successorIter = mapIter->second.d_successors.begin();
873 successorIter != mapIter->second.d_successors.end();
874 ++successorIter) {
875
876 LinksMapIter succMapIter = d_workSet.find((*successorIter));
877 Links& succLinks = succMapIter->second;
878
879 // One less predecessor now.
880 if (--(succLinks.d_predecessorCount) == 0) { // No more predecessors?
881 // This successor node is ready.
882 d_readyNodes.push(succMapIter);
883 }
884 }
885
886 d_readyNodes.pop(); // Drop the map iterator from the FIFO.
887 d_workSet.erase(mapIter); // Remove the processed node from the work set.
888
889 return true;
890}
891
892template <class INPUT_ITER>
893template <class OUTPUT_ITER, class UNSORTED_OUT_ITER>
895 OUTPUT_ITER resultOutIter,
896 UNSORTED_OUT_ITER unsortedOutIter)
897{
898 while (!d_workSet.empty()) {
899 if (!processNextNodeInOrder(&resultOutIter)) {
900 // A cycle was detected. Write all the nodes still present in the
901 // work set to 'unsortedOutIter'. The nodes in the work set may be
902 // in more than one cycle, or interconnected cycles, but our
903 // algorithm ensures that all nodes left here *are* part of at
904 // least one cycle.
905
906 const LinksMapConstIter end = d_workSet.end();
907 for (LinksMapConstIter i = d_workSet.begin(); i != end; ++i) {
908 *unsortedOutIter = i->first;
909 ++unsortedOutIter;
910 }
911 return false; // RETURN
912 }
913 }
914 return true;
915}
916
917 // -------------------------
918 // class TopologicalSortUtil
919 // -------------------------
920
921// MANIPULATORS
922template <class INPUT_ITER, class OUTPUT_ITER, class UNSORTED_OUT_ITER>
923bool TopologicalSortUtil::sort(INPUT_ITER relationsBegin,
924 INPUT_ITER relationsEnd,
925 OUTPUT_ITER resultOutIter,
926 UNSORTED_OUT_ITER unsortedOutIter)
927{
929
930 return MyHelper(relationsBegin, relationsEnd).sortImpl(resultOutIter,
931 unsortedOutIter);
932}
933
934template <class NODE_TYPE>
937 bsl::vector<NODE_TYPE> *unsorted,
939{
940 typedef bsl::vector<NODE_TYPE> vector_type;
941
942 return sort(relations.begin(),
943 relations.end(),
944 bsl::back_insert_iterator<vector_type>(*result),
945 bsl::back_insert_iterator<vector_type>(*unsorted));
946}
947
948} // close package namespace
949
950
951#endif
952
953// ----------------------------------------------------------------------------
954// Copyright 2018 Bloomberg Finance L.P.
955//
956// Licensed under the Apache License, Version 2.0 (the "License");
957// you may not use this file except in compliance with the License.
958// You may obtain a copy of the License at
959//
960// http://www.apache.org/licenses/LICENSE-2.0
961//
962// Unless required by applicable law or agreed to in writing, software
963// distributed under the License is distributed on an "AS IS" BASIS,
964// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
965// See the License for the specific language governing permissions and
966// limitations under the License.
967// ----------------------------- END-OF-FILE ----------------------------------
968
969/** @} */
970/** @} */
971/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlb_topologicalsortutil.h:572
bool sortImpl(OUTPUT_ITER resultOutIter, UNSORTED_OUT_ITER unsortedOutIter)
Definition bdlb_topologicalsortutil.h:894
bool processNextNodeInOrder(OUTPUT_ITER *resultOutIter_p)
Definition bdlb_topologicalsortutil.h:852
BSLMF_NESTED_TRAIT_DECLARATION(TopologicalSortUtil_Helper, bslma::UsesBslmaAllocator)
Definition bslstl_pair.h:1210
Definition bslstl_queue.h:304
void push(const value_type &value)
Definition bslstl_queue.h:775
Definition bslstl_unorderedmap.h:1089
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_unorderedmap.h:2894
BloombergLP::bslstl::HashTableIterator< value_type, difference_type > iterator
Definition bslstl_unorderedmap.h:1158
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_unorderedmap.h:2885
BloombergLP::bslstl::HashTableIterator< const value_type, difference_type > const_iterator
Definition bslstl_unorderedmap.h:1160
Definition bslstl_vector.h:1025
NodeType * iterator
Definition bslstl_vector.h:1057
NodeType const * const_iterator
Definition bslstl_vector.h:1058
Definition bslma_allocator.h:457
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlb_algorithmworkaroundutil.h:74
Definition bdlb_printmethods.h:283
NODE_TYPE NodeType
The type of values of the nodes of the directed acyclic graph.
Definition bdlb_topologicalsortutil.h:544
bsl::pair< NODE_TYPE, NODE_TYPE > EdgeType
The type of the directed connection from one node to another.
Definition bdlb_topologicalsortutil.h:541
Definition bdlb_topologicalsortutil.h:530
Definition bdlb_topologicalsortutil.h:693
static bool sort(INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, OUTPUT_ITER resultOutIter, UNSORTED_OUTPUT_ITER unsortedOutIter)
TYPE first
Definition bslstl_pair.h:605
TYPE second
Definition bslstl_pair.h:908
Definition bslma_usesbslmaallocator.h:343