BDE 4.39.x 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 /// specified `input`.
520///
521/// \note Note that the template parameter type
522/// /// `EDGE_TYPE` is an element in the input range to `sort`.
523/// static const NodeType& from(const EDGE_TYPE& input);
524///
525/// /// Return a `const` reference to the "from" attribute of the /// specified `input`.
526///
527/// \note Note that the template parameter type
528/// /// `EDGE_TYPE` is an element in the input range to `sort`.
529/// static const NodeType& to(const EDGE_TYPE& input)
530/// @endcode
531///
532/// See @ref bdlb_topologicalsortutil
533template <class EDGE_TYPE>
536
537/// This `struct` is a specialization (customization) of
538/// `TopologicalSortUtilEdgeTraits` for bsl::pair<T, T>.
539template <class NODE_TYPE>
540struct TopologicalSortUtilEdgeTraits<bsl::pair<NODE_TYPE, NODE_TYPE> > {
541
542 // TYPES
543
544 /// The type of the directed connection from one node to another
546
547 /// The type of values of the nodes of the directed acyclic graph
548 typedef NODE_TYPE NodeType;
549
550 // CLASS METHODS
551
552 /// Return a `const` reference to the `from` attribute of the specified
553 /// `edge` object.
554 static const NODE_TYPE& from(const EdgeType& edge);
555
556 /// Return a `to` reference to the `from` attribute of the specified
557 /// `edge` object.
558 static const NODE_TYPE& to(const EdgeType& edge);
559};
560
561 // ================================
562 // class TopologicalSortUtil_Helper
563 // ================================
564
565/// This class template provides data structures required to sort the
566/// partial unsorted edges into a total ordered set of nodes. The value
567/// type of the nodes(*) must be hashable and equality comparable by
568/// `bsl::hash`, and `bsl::equal_to` respectively.
569///
570/// * (*) The value type is determined by first getting the iterator's value
571/// type using `bsl::iterator_traits<INPUT_ITER>::ValueType` and then
572/// using `TopologicalSortUtilEdgeTraits::ValueType` on that result.
573///
574/// See @ref bdlb_topologicalsortutil
575template <class INPUT_ITER>
577
578 private:
579 // PRIVATE TYPES
580 typedef bsl::iterator_traits<INPUT_ITER> InIterTraits;
581 typedef typename InIterTraits::value_type EdgeType;
583
584 /// Get the traits type that corresponds to the incoming edge type, then
585 /// the types from it, with short names.
586 typedef typename EdgeTraits::NodeType NodeType;
587
589 typedef typename List::iterator ListIter;
590
591 /// An ordered list of nodes and their iterators
592 typedef typename List::const_iterator ListConstIter;
593
594 /// Relations information for a given node in the format necessary for
595 /// topological sorting. The attributes are:
596 ///
597 /// * **the predecessor count**:
598 /// How many need to be sorted before this node comes in order?
599 ///
600 /// * **the successor nodes**:
601 /// All the nodes that must come after this node in the order
602 ///
603 /// See @ref bdlb_topologicalsortutil
604 struct Links {
605
606 // PUBLIC DATA
607 int d_predecessorCount; // How many are before us?
608 List d_successors; // those that are after us
609
610 // TRAITS
612
613 // CREATORS
614
615 /// Create a `Links` which holds empty predecessor and successor
616 /// information. Optionally, specify an `allocator` for needed
617 /// memory. If `allocator` is 0, use the globally supply default
618 /// allocator instead.
619 explicit Links(bslma::Allocator *allocator = 0);
620
621 /// Create a `Links` object having the same attribute values as the
622 /// specified `original` object. Optionally specify an `allocator`
623 /// used to supply memory. If `allocator` is 0, the currently
624 /// installed default allocator is used.
625 Links(const Links& original, bslma::Allocator *allocator = 0);
626 };
627
629 typedef typename LinksMap::iterator LinksMapIter;
630
631 /// Mapping of nodes to their collected relations-information
632 typedef typename LinksMap::const_iterator LinksMapConstIter;
633
634 /// FIFO queue of nodes to process.
635 /// \note Note that it is safe to store
636 /// iterators into the work-set/mapping in the queue because
637 /// `bsl::unordered_map` guarantees the stability of every iterator
638 /// after a delete, except the iterators pointing to the deleted entry
639 /// itself.
641
642 private:
643 // DATA
644 LinksMap d_workSet; // mapping from nodes to their relations
645 // information (predecessorCount, successors)
646
647 Fifo d_readyNodes; // elements whose predecessors have all already
648 // been sorted (or never had predecessors) as
649 // iterators into the mapping (`d_workSet`)
650
651 private:
652 // NOT IMPLEMENTED
655
656 public:
657 // TRAITS
660
661 // CREATORS
662
663 /// Create a helper class that holds the different data structures
664 /// required to sort in topological order the directed acyclic graph
665 /// described by the specified `relationsBegin` and `relationsEnd`.
666 /// Optionally, specify an `allocator` for needed memory. If
667 /// `allocator` is 0, use the globally supply default allocator instead.
668 explicit TopologicalSortUtil_Helper(INPUT_ITER relationsBegin,
669 INPUT_ITER relationsEnd,
670 bslma::Allocator *allocator = 0);
671
672 // MANIPULATORS
673
674 /// Write the next element in order that doesn't have any predecessors
675 /// into the specified `resultOutIter_p` output iterator then increment
676 /// it and return `true`. If there are still elements left but all of
677 /// them still have predecessors do nothing and return `false`.
678 ///
679 /// \pre The behavior is undefined unless `!d_workSet.empty()`.
680 template <class OUTPUT_ITER>
681 bool processNextNodeInOrder(OUTPUT_ITER *resultOutIter_p);
682
683 /// Sort the input elements provided during construction in topological
684 /// order and write the resulting linear ordered set into the specified
685 /// `resultOutIter` and return `true`. If the sort is unsuccessful (the
686 /// input is not an acyclic directed graph) write the nodes that have
687 /// not been sorted to the specified `unsortedOutIter` output and return
688 /// `false`. See `TopologicalSortUtil::sort` "iterators overload" for
689 /// more detailed specification.
690 template <class OUTPUT_ITER, class UNSORTED_OUT_ITER>
691 bool sortImpl(OUTPUT_ITER resultOutIter,
692 UNSORTED_OUT_ITER unsortedOutIter);
693};
694
695 // =====================
696 // class TopologicalSort
697 // =====================
698
699/// This `struct` `TopologicalSortUtil` provides a namespace for topological
700/// sorting functions.
701///
702/// See @ref bdlb_topologicalsortutil
704
705 public:
706 // CLASS METHODS
707
708 /// Sort the input elements in topological order and write the resulting
709 /// linear ordered set into the specified `resultOutIter`. If the
710 /// sort is unsuccessful (the input is not an acyclic directed graph)
711 /// write the elements that have not been sorted to the specified
712 /// `unsortedOutIter` output. The input elements are provided as a
713 /// sequence of (conceptual or physical) pairs between the specified
714 /// `relationsBegin` and `relationsEnd` of the form (U, V), where U and
715 /// V are nodes, and U precedes V in the output. Return `true` on
716 /// success, and `false` if the sort fails due to a cycle in the input.
717 /// The type `bsl::iterator_traits<INPUT_ITER>::value_type` must either
718 /// be `bsl` or `std::pair` where the @ref first_type and @ref second_type are
719 /// the same as `bsl::iterator_traits<OUTPUT_ITER>::value_type` or
720 /// `TopologicalSortUtilEdgeTraits` must be specialized for the type,
721 /// i.e., the supplied `bsl::iterator_traits<INPUT_ITER>::value_type`
722 /// must support the following syntax:
723 /// @code
724 /// typedef typename bsl::iterator_traits<INPUT_ITER>::value_type
725 /// IterValue;
726 /// typedef typename bsl::iterator_traits<RESULT_ITER>::value_type
727 /// ResultValue;
728 ///
729 /// typedef typename TopologicalSortUtilEdgeTraits<IterValue> Traits;
730 ///
731 /// typedef typename Traits::NodeType NodeType;
732 ///
733 /// for (INPUT_ITER it = relationshipPairsBegin;
734 /// it != relationshipPairsEnd;
735 /// ++it) {
736 ///
737 /// *result++ = Traits::from(*it); // U
738 /// *result++ = Traits::to(*it); // V
739 /// }
740 /// @endcode
741 ///
742 /// \note Note that when the method returns `false`, `resultOutIter` may
743 /// contain a subset of the elements in the right topological order,
744 /// essentially the elements that the routine was able to sort before
745 /// the cycle was discovered. In that case, the elements that the
746 /// routine was unable to sort were written to the `unsortedOutIter`
747 /// output iterator, in an unspecified order.
748 template <class INPUT_ITER,
749 class OUTPUT_ITER,
750 class UNSORTED_OUTPUT_ITER>
751 static bool sort(INPUT_ITER relationsBegin,
752 INPUT_ITER relationsEnd,
753 OUTPUT_ITER resultOutIter,
754 UNSORTED_OUTPUT_ITER unsortedOutIter);
755
756 /// Sort the elements of `NODE_TYPE` in topological order determined by
757 /// the specified `relations` and load the resulting linear ordered set
758 /// to the specified `result`. If the sort is unsuccessful, load the
759 /// elements that have not been ordered to the specified `unsorted`
760 /// list. The input relations are provided as pairs of the form (U, V)
761 /// where U precedes V in the output. Return `false` if sort fails due
762 /// to a cycle in the input else return `true` if sort successful.
763 ///
764 /// \note Note that even if the method returns `false`, `result` may contain a
765 /// subset of elements in the right topological order, essentially
766 /// elements that the routine was able to sort before the cycle was
767 /// discovered and `unsorted` will contain the elements that the
768 /// routine was unable to sort.
769 template <class NODE_TYPE>
770 static bool sort(
772 bsl::vector<NODE_TYPE> *unsorted,
774};
775
776// ============================================================================
777// INLINE AND TEMPLATE FUNCTION DEFINITIONS
778// ============================================================================
779
780 // -----------------------------------
781 // class TopologicalSortUtilEdgeTraits
782 // -----------------------------------
783
784template <class NODE_TYPE>
785inline
786const NODE_TYPE&
788from(const EdgeType& edge)
789{
790 return edge.first;
791}
792
793template <class NODE_TYPE>
794inline
795const NODE_TYPE&
797to(const EdgeType& edge)
798{
799 return edge.second;
800}
801
802
803 // ------------------------------------------
804 // class TopologicalSortUtil_Helper::NodeInfo
805 // ------------------------------------------
806
807// CREATORS
808template <class INPUT_ITER>
809inline
811 bslma::Allocator *allocator)
812: d_predecessorCount(0)
813, d_successors(allocator)
814{
815}
816
817template <class INPUT_ITER>
818inline
819TopologicalSortUtil_Helper<INPUT_ITER>::Links::Links(
820 const Links& original,
821 bslma::Allocator *allocator)
822: d_predecessorCount(original.d_predecessorCount)
823, d_successors(original.d_successors, allocator)
824{
825}
826 // --------------------------------
827 // class TopologicalSortUtil_Helper
828 // --------------------------------
829
830// CREATORS
831template <class INPUT_ITER>
833 INPUT_ITER relationsBegin,
834 INPUT_ITER relationsEnd,
835 bslma::Allocator *allocator)
836: d_workSet(allocator)
837, d_readyNodes(allocator)
838{
839 // Iterate through the input list of edges and iteratively fill in the
840 // relations information needed for sorting.
841
842 for (INPUT_ITER iter = relationsBegin; iter != relationsEnd; ++iter) {
843 const NodeType& from = EdgeTraits::from(*iter);
844 const NodeType& to = EdgeTraits::to(*iter);
845 ++d_workSet[to].d_predecessorCount;
846 d_workSet[from].d_successors.push_back(to);
847 }
848
849 // Now iterate through the set and find nodes that are roots, i.e. which
850 // don't have any predecessors and hence are the first in order. We put
851 // these nodes into a queue that is ready to be written to the result.
852
853 const LinksMapIter end = d_workSet.end();
854 for (LinksMapIter iter = d_workSet.begin(); iter != end; ++iter) {
855 if (0 == iter->second.d_predecessorCount) {
856 d_readyNodes.push(iter);
857 }
858 }
859}
860
861// MANIPULATORS
862template <class INPUT_ITER>
863template <class OUTPUT_ITER>
865 OUTPUT_ITER *resultOutIter_p)
866{
867 if (d_readyNodes.empty()) {
868 // If the queue is empty but the input set is not then we have at least
869 // one cycle. We know *here* that the input set is not empty because
870 // it is a precondition for calling this function.
871 return false; // RETURN
872 }
873
874 // process the next element (in 'd_readyNodes' FIFO) with no predecessors
875 const LinksMapIter mapIter = d_readyNodes.front();
876
877 // move the processed node to the sorted output
878 *(*resultOutIter_p) = mapIter->first;
879 ++(*resultOutIter_p); // output iterator must be moved after a write
880
881 // Iterate through the successor list of the node and reduce predecessor
882 // count of each successor. Further, if that count becomes zero add that
883 // successor to 'd_readyNodes'.
884 for (ListIter successorIter = mapIter->second.d_successors.begin();
885 successorIter != mapIter->second.d_successors.end();
886 ++successorIter) {
887
888 LinksMapIter succMapIter = d_workSet.find((*successorIter));
889 Links& succLinks = succMapIter->second;
890
891 // One less predecessor now.
892 if (--(succLinks.d_predecessorCount) == 0) { // No more predecessors?
893 // This successor node is ready.
894 d_readyNodes.push(succMapIter);
895 }
896 }
897
898 d_readyNodes.pop(); // Drop the map iterator from the FIFO.
899 d_workSet.erase(mapIter); // Remove the processed node from the work set.
900
901 return true;
902}
903
904template <class INPUT_ITER>
905template <class OUTPUT_ITER, class UNSORTED_OUT_ITER>
907 OUTPUT_ITER resultOutIter,
908 UNSORTED_OUT_ITER unsortedOutIter)
909{
910 while (!d_workSet.empty()) {
911 if (!processNextNodeInOrder(&resultOutIter)) {
912 // A cycle was detected. Write all the nodes still present in the
913 // work set to 'unsortedOutIter'. The nodes in the work set may be
914 // in more than one cycle, or interconnected cycles, but our
915 // algorithm ensures that all nodes left here *are* part of at
916 // least one cycle.
917
918 const LinksMapConstIter end = d_workSet.end();
919 for (LinksMapConstIter i = d_workSet.begin(); i != end; ++i) {
920 *unsortedOutIter = i->first;
921 ++unsortedOutIter;
922 }
923 return false; // RETURN
924 }
925 }
926 return true;
927}
928
929 // -------------------------
930 // class TopologicalSortUtil
931 // -------------------------
932
933// MANIPULATORS
934template <class INPUT_ITER, class OUTPUT_ITER, class UNSORTED_OUT_ITER>
935bool TopologicalSortUtil::sort(INPUT_ITER relationsBegin,
936 INPUT_ITER relationsEnd,
937 OUTPUT_ITER resultOutIter,
938 UNSORTED_OUT_ITER unsortedOutIter)
939{
941
942 return MyHelper(relationsBegin, relationsEnd).sortImpl(resultOutIter,
943 unsortedOutIter);
944}
945
946template <class NODE_TYPE>
949 bsl::vector<NODE_TYPE> *unsorted,
951{
952 typedef bsl::vector<NODE_TYPE> vector_type;
953
954 return sort(relations.begin(),
955 relations.end(),
956 bsl::back_insert_iterator<vector_type>(*result),
957 bsl::back_insert_iterator<vector_type>(*unsorted));
958}
959
960} // close package namespace
961
962
963#endif
964
965// ----------------------------------------------------------------------------
966// Copyright 2018 Bloomberg Finance L.P.
967//
968// Licensed under the Apache License, Version 2.0 (the "License");
969// you may not use this file except in compliance with the License.
970// You may obtain a copy of the License at
971//
972// http://www.apache.org/licenses/LICENSE-2.0
973//
974// Unless required by applicable law or agreed to in writing, software
975// distributed under the License is distributed on an "AS IS" BASIS,
976// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
977// See the License for the specific language governing permissions and
978// limitations under the License.
979// ----------------------------- END-OF-FILE ----------------------------------
980
981/** @} */
982/** @} */
983/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlb_topologicalsortutil.h:576
bool sortImpl(OUTPUT_ITER resultOutIter, UNSORTED_OUT_ITER unsortedOutIter)
Definition bdlb_topologicalsortutil.h:906
bool processNextNodeInOrder(OUTPUT_ITER *resultOutIter_p)
Definition bdlb_topologicalsortutil.h:864
BSLMF_NESTED_TRAIT_DECLARATION(TopologicalSortUtil_Helper, bslma::UsesBslmaAllocator)
Definition bslstl_pair.h:1280
Definition bslstl_queue.h:324
void push(const value_type &value)
Definition bslstl_queue.h:955
Definition bslstl_unorderedmap.h:1123
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_unorderedmap.h:3386
BloombergLP::bslstl::HashTableIterator< value_type, difference_type > iterator
Definition bslstl_unorderedmap.h:1233
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_unorderedmap.h:3377
BloombergLP::bslstl::HashTableIterator< const value_type, difference_type > const_iterator
Definition bslstl_unorderedmap.h:1235
Definition bslstl_vector.h:1120
NodeType * iterator
Definition bslstl_vector.h:1152
NodeType const * const_iterator
Definition bslstl_vector.h:1153
Definition bslma_allocator.h:545
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlb_algorithmworkaroundutil.h:74
Definition bdlat_valuetypefunctions.h:939
NODE_TYPE NodeType
The type of values of the nodes of the directed acyclic graph.
Definition bdlb_topologicalsortutil.h:548
bsl::pair< NODE_TYPE, NODE_TYPE > EdgeType
The type of the directed connection from one node to another.
Definition bdlb_topologicalsortutil.h:545
Definition bdlb_topologicalsortutil.h:534
Definition bdlb_topologicalsortutil.h:703
static bool sort(INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, OUTPUT_ITER resultOutIter, UNSORTED_OUTPUT_ITER unsortedOutIter)
TYPE first
Definition bslstl_pair.h:587
TYPE second
Definition bslstl_pair.h:933
Definition bslma_usesbslmaallocator.h:344