// bdlb_topologicalsortutil.h -*-C++-*- #ifndef INCLUDED_BDLB_TOPOLOGICALSORTUTIL #define INCLUDED_BDLB_TOPOLOGICALSORTUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a utility to topologically sort a collection of inputs. // //@CLASSES: // bdlb::TopologicalSortUtil: utility for topologically sorting inputs // bdlb::TopologicalSortUtilEdgeTraits: customization point for edge types // //@SEE_ALSO: // //@DESCRIPTION: This component provides a utility 'struct', // 'bdlb::TopologicalSortUtil', to topologically sort a collection of inputs // that describe a directed acyclic graph (also known as DAG). A topological // sort is useful for defining the order in which an input should be processed // based on certain conditions. As an example, consider two jobs B and C both // of which have a dependency on job A, i.e., job A needs to be completed // before B or C can be run. Given such a requirement, a topological sort can // provide an ordering of the three jobs such that A precedes B and C. Note // that there may be multiple orderings that might be correct (e.g.: A, B, then // C or A, C then B; both are correct and satisfy the dependencies of A running // before the B and C jobs; we don't care whether B precedes C or vice versa). // // The dependencies are specified in pairs (U,V) (read as U precedes V), which // define the relations between U and V that the sort needs to maintain. The // elements in these pairs (e.g., U and V) make up a finite set S. The output // of the topological sort is an ordering of all elements of set S, such that // all relations specified in the input (as pairs) are satisfied. Note that // such an ordering is only possible only if there are no cycles in the input // dependencies. For example, if A depends on B and B depends on A, then it is // not possible to order A and B satisfying both the relations. The routine // 'sort' defined in this component returns 'false' if it detects a cycle while // trying to sort the input. // // Complexity of the topological sort in this component is 'O(n+m)' where n is // the number of input elements in the finite set S and m is the number of // relations as specified by the input pairs. // // The 'sort' function is provided in two variants or flavors: simple and // iterator-based. The simple flavor is templated on the 'NODE_TYPE', and it // provides topological sorting for the case where the input is a 'bsl::vector' // of 'bsl::pair's, and the outputs are 'bsl::vector's. The templated variant // is highly customizable (templated) on both the input and the outputs. The // input may be any input iterator range that has a 'bsl::pair' 'value_type' or // a 'value_type' with a 'bdlb::TopologicalSortUtilEdgeTraits' specialization. // The 'value_type' is determined using 'bsl::iterator::traits'. The two // outputs are both defined as templated output iterators and they may have // different types. So (for example) the iterator based 'sort' may be used // with Null 'OUTPUT_ITER' to answer the question "does this graph have // cycles?" while not wasting memory in storing the sort results. // ///Self-referencing Nodes ///---------------------- // Some graph representations may use self-referencing nodes to indicate nodes // without any connection -- where a self referencing node in the context of // this component is a input edge pair like (u, u). The implementation of // 'sort' in this component treats such input entries as cycles and fails // sorting. // // You may still use this 'sort' implementation (to process your nodes in // proper order) if your data structure contains such entries. To process a // graph having self-referencing nodes one may create a filtering iterator on // top of the input that removes any self-referential edges from the input. // Any filtered nodes could be treated as being sorted first. // ///Concepts ///-------- // This component provides generic (templated) utilities. This section // describes the requirements of the type parameters (concepts) of these // generic methods. // ///'NODE_TYPE' Concept ///- - - - - - - - - - // The input for a topological sort is supplied as pairs (though not // necessarily 'bsl::pair's) of 'NODE_TYPE' values. 'NODE_TYPE' is aliased to // 'NodeType' in 'TopologicalSortUtilEdgeTraits'. // // 'NODE_TYPE' shall be a value type that supports hashing using the // 'bsl::hash<NODE_TYPE>' functor type and equality comparison using the // 'bsl::equal_to<NODE_TYPE>' functor type. // // Notice that we have not provided a customization point for the hash functor // type nor for the equality functor type because that would complicate the // code greatly (esp. readability would suffer). // ///'EdgeType' Concept /// - - - - - - - - - // The 'EdgeType' describes an edge as a (conceptual) pair of 'NODE_TYPE' // values and is supplied as the input to the topological sort methods. // 'EdgeType' is the 'value_type' of the 'INPUT_ITER' supplied to the 'sort' // functions. Conceptually it represents a pair (U,V) (where U and V are // 'NodeType's) that means "U precedes V". The 'EdgeType' supplied to a sort // is typically a 'bsl::pair', but users may customize this by specializing the // 'TopologicalSortUtilEdgeTraits' type. 'TopologicalSortUtil' determines the // 'NodeType' and access operations on 'EdgeType' via the // 'TopologicalSortUtilEdgeTraits' type. // // 'TopologicalSortUtilEdgeTraits' is specialized for 'bsl::pair<T,T>', the // user needs to (partially or fully) specialize it for other types. See the // 'CustomEdge' specialization in the test driver for an example. // ///'INPUT_ITER' Concept /// - - - - - - - - - - // 'INPUT_ITER' is an input iterator type used by the 'sort' functions, and its // 'value_type' is 'EdgeType' . // ///'OUTPUT_ITER' Concept ///- - - - - - - - - - - // 'OUTPUT_ITER' is an output iterator for 'sort' functions, and its // 'value_type' is 'NodeType'. // ///'UNORDERED_ITER' Concept /// - - - - - - - - - - - - // // 'UNORDERED_ITER' is a (second) output iterator for 'sort' functions, and its // 'value_type' is 'NodeType'. Note that 'OUTPUT_ITERATOR' and // 'UNORDERED_ITER' are distinct types allowing the sorted output nodes to be // collected in a different way from the unsorted output nodes(e.g., into a // different type of container). // ///USAGE: ///----- // This section illustrates intended use of this component. // ///Example 1: Using Topological Sort for Calculating Formulas /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we are evaluating formulas for a set of market data fields, where // formulas can reference other fields as part of their calculation. As an // example, let's say we have a field 'k_bbgDefinedVwap' that is dependent on // 'k_vwapTurnover' and 'k_vwapVolume'. These fields in turn are dependent on // 'k_tradeSize' and 'k_tradePrice' respectively. So essentially the fields // that are not dependent on any other field should be calculated first and // then the dependent fields. We can use the topological sort utility to // provide us the order in which these fields should be calculated. // // First, we create the relations showcasing the above mentioned dependencies // in the form of pairs (a,b) where b is dependent on a: //.. // enum FieldIds { // k_bbgDefinedVwap = 0, // k_vwapTurnover = 1, // k_vwapVolume = 2, // k_tradeSize = 3, // k_tradePrice = 4 // }; // // bsl::vector<bsl::pair<int, int> > relations; // // relations.push_back(bsl::make_pair(static_cast<int>(k_vwapTurnover), // static_cast<int>(k_bbgDefinedVwap))); // relations.push_back(bsl::make_pair(static_cast<int>(k_vwapVolume), // static_cast<int>(k_bbgDefinedVwap))); // relations.push_back(bsl::make_pair(static_cast<int>(k_tradeSize), // static_cast<int>(k_vwapVolume))); // relations.push_back(bsl::make_pair(static_cast<int>(k_tradeSize), // static_cast<int>(k_vwapTurnover))); // relations.push_back(bsl::make_pair(static_cast<int>(k_tradePrice), // static_cast<int>(k_vwapTurnover))); //.. // Now, we call the topological sort to get a topological order for the fields // referenced in the relations: //.. // bsl::vector<int> results; // bsl::vector<int> unsorted; // bool sorted = TopologicalSortUtil::sort(&results, // &unsorted, // relations); //.. // Finally, we verify that the call to 'sort' populates the supplied 'results' // with a sequence in sorted order (e.g.. 'k_tradeSize', 'k_tradePrice, // 'k_vwapTurnover', 'k_vwapVolume', 'k_bbgDefinedVwap') and 'unsorted' will be // empty because the input relationships do not contain a cycle. //.. // bool calculated[5] = { 0 }; // assert(sorted == true); // assert(unsorted.empty()); // // for (bsl::vector<int>::const_iterator iter = results.begin(), // end = results.end(); // iter != end; ++iter) { // switch (*iter) { // case k_bbgDefinedVwap: { // assert(calculated[k_vwapTurnover] == true); // assert(calculated[k_vwapVolume] == true); // // assert(calculated[k_bbgDefinedVwap] == false); // calculated[k_bbgDefinedVwap] = true; // } break; // case k_vwapTurnover: { // assert(calculated[k_tradeSize] == true); // assert(calculated[k_tradePrice] == true); // // assert(calculated[k_vwapTurnover] == false); // calculated[k_vwapTurnover] = true; // } break; // case k_vwapVolume: { // assert(calculated[k_tradeSize] == true); // // assert(calculated[k_vwapVolume] == false); // calculated[k_vwapVolume] = true; // } break; // case k_tradeSize: { // assert(calculated[k_vwapVolume] == false); // assert(calculated[k_vwapTurnover] == false); // // assert(calculated[k_tradeSize] == false); // calculated[k_tradeSize] = true; // } break; // case k_tradePrice: { // assert(calculated[k_vwapTurnover] == false); // // assert(calculated[k_tradePrice] == false); // calculated[k_tradePrice] = true; // } break; // default: // assert(false); // break; // }; // } // // for (int i = 0; i < 5; ++i) { // assert(calculated[i] == true); // } //.. ///Example 2: Using Topological Sort with Cycles in Input /// - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we have a set of inputs that contain a cycle. // // First, we define a set of inputs that have a cycle: //.. // enum FieldIds2 { // k_FIELD1 = 0, // k_FIELD2 = 1, // k_FIELD3 = 2 // }; // // bsl::vector<bsl::pair<int, int> > relations2; // // relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD2), // static_cast<int>(k_FIELD1))); // relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD3), // static_cast<int>(k_FIELD2))); // relations2.push_back(bsl::make_pair(static_cast<int>(k_FIELD1), // static_cast<int>(k_FIELD3))); //.. // Now, we apply the topological sort routine on the input: //.. // bsl::vector<int> results2; // bsl::vector<int> unordered2; // bool sorted2 = TopologicalSortUtil::sort(&results2, // &unordered2, // relations2); //.. // Finally, we verify whether the routine recognizes that there is a cycle and // returns false, and the 3 nodes comprising the cycle are reported in // 'unordered2': //.. // assert(sorted2 == false); // assert(unordered2.size() == 3); //.. ///Example 3: Using Topological Sort with Self Relations ///- - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we have a set of inputs that have input relations where predecessor // and successor point to the same value, i.e. we have pairs of input like // (u,u). This example demonstrates that the 'sort' function handles such // input as a cycle. (See Example 6 for a way of processing such nodes and // avoiding 'sort' reporting a cycle.) First, we define such a set of inputs: //.. // enum FieldIds3 { // k_FIELD4 = 3, // k_FIELD5 = 4, // k_FIELD6 = 5 // }; // // bsl::vector<bsl::pair<int, int> > relations3; // // relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD4), // static_cast<int>(k_FIELD6))); // relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD5), // static_cast<int>(k_FIELD4))); // relations3.push_back(bsl::make_pair(static_cast<int>(k_FIELD4), // static_cast<int>(k_FIELD4))); //.. // Now, we apply the topological sort routine on the input: //.. // bsl::vector<int> results3; // bsl::vector<int> unordered3; // bool sorted3 = TopologicalSortUtil::sort(&results3, // &unordered3, // relations3); //.. // Finally, we verify that the self relations causes the cycle: //.. // assert(sorted3 == false); // assert(results3.size() == 1); // assert(unordered3.size() == 2); // // if (veryVeryVerbose) { // cout << "Size of results3 vector is " // << results3.size() << endl; // // cout << "Size of unordered3 vector is " // << unordered3.size() << endl; // } // // if (veryVeryVerbose) // { // for (bsl::size_t i = 0; i < results3.size(); ++i) { // cout << "results3[" << i << "] is " << results3[i] << "\n"; // } // // for (bsl::size_t i = 0; i < unordered3.size(); ++i) { // cout << "unordered3[" << i << "] is " << unordered3[i] << "\n"; // } // } //.. ///Example 4: Using Topological Sort with Iterators as Input ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we have a set of inputs that have input relations that conceptually // follow the input requirements (of a listing of pairs of nodes) but are not // physically stored in a 'bsl::vector' of 'bsl::pair' typed container. Let's // suppose the input is in a 'bsl::list' instead. First, we define such a set // of inputs: //.. // bsl::list<bsl::pair<int, int> > relations4; // // relations4.push_back(bsl::make_pair(1, 2)); // relations4.push_back(bsl::make_pair(1, 3)); // relations4.push_back(bsl::make_pair(2, 3)); //.. // Now, we apply the topological sort routine on the input: //.. // bsl::vector<int> results4; // bsl::vector<int> unordered4; // typedef bsl::back_insert_iterator<bsl::vector<int> > OutIter; // bool sorted4 = TopologicalSortUtil::sort(relations4.begin(), // relations4.end(), // OutIter(results4), // OutIter(unordered3)); //.. // Finally, we verify that the sort is successful, there are no nodes in the // 'unsorted' output (there is no cycle) and the nodes are listed in the // proper order in 'results4': //.. // assert(sorted4 == true); // assert(unordered4.size() == 0); // assert(results4.size() == 3); // // assert(results4[0] == 1); // assert(results4[1] == 2); // assert(results4[2] == 3); //.. ///Example 5: Using Topological Sort with Iterators as Output ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we want our result in a 'bsl::list' instead of a 'bsl::vector' and // we do not care about the unsorted elements so we do not want to pay for // storing them if they exist. First, we would define a Null Output Iterator // that writes to nowhere: //.. // class NullOutputIterator { // public: // typedef void container_type; // typedef void value_type; // typedef void difference_type; // typedef void pointer; // typedef void reference; // typedef bsl::output_iterator_tag iterator_category; // // template <class T> // NullOutputIterator& operator=(const T&) // { // return *this; // } // // NullOutputIterator& operator*() // { // return *this; // } // // NullOutputIterator& operator++() // { // return *this; // } // // NullOutputIterator& operator++(int) // { // return *this; // } // }; //.. // Now, we apply the topological sort routine on the input: //.. // bsl::list<int> results5; // typedef bsl::back_insert_iterator<bsl::list<int> > ListOutIter; // bool sorted5 = TopologicalSortUtil::sort(relations4.begin(), // relations4.end(), // ListOutIter(results5), // NullOutputIterator()); //.. // Finally, we verify that the sort is successful, and the 3 nodes are listed // in the proper order in the 'results5' list: //.. // assert(sorted5 == true); // assert(results5.size() == 3); // // assert(results5[0] == 1); // assert(results5[1] == 2); // assert(results5[2] == 3); //.. #include <bdlscm_version.h> #include <bslma_allocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bslmf_istriviallycopyable.h> #include <bslmf_istriviallydefaultconstructible.h> #include <bsls_assert.h> #include <bsl_iterator.h> #include <bsl_queue.h> #include <bsl_unordered_map.h> #include <bsl_utility.h> #include <bsl_vector.h> #include <utility> // for 'std::pair' namespace BloombergLP { namespace bdlb { // =================================== // class TopologicalSortUtilEdgeTraits // =================================== template <class EDGE_TYPE> struct TopologicalSortUtilEdgeTraits { // This 'struct' represents a customization point allowing clients to // supply input iterators to 'sort' having a 'value_types' other than a // 'bsl::pair'. Clients may specialize 'TopologicalSortUtilEdgeTraits' to // supply the following: //.. // typedef EDGE_TYPE EdgeType; // // The type of the directed connection from one node to another // // 'bsl::pair<NodeType, NodeType>' and 'std::pair<NodeType>' work // // without 'TopologicalSortUtilEdgeTraits' specialization. // // typedef user-defined NodeType; // // Alias describing the output values from a sort, as well as the // // results of the 'from' and 'to' functions of this edge traits // // instance. Or in other words, the node (or node identifier) type // // of the directed acyclic graph. // // static const NodeType& from(const EDGE_TYPE& input); // // Return a 'const' reference to the "from" attribute of the // // specified 'input'. Note that the template parameter type // // 'EDGE_TYPE' is an element in the input range to 'sort'. // // static const NodeType& to(const EDGE_TYPE& input) // // Return a 'const' reference to the "from" attribute of the // // specified 'input'. Note that the template parameter type // // 'EDGE_TYPE' is an element in the input range to 'sort'. //.. }; template <class NODE_TYPE> struct TopologicalSortUtilEdgeTraits<bsl::pair<NODE_TYPE, NODE_TYPE> > { // This 'struct' is a specialization (customization) of // 'TopologicalSortUtilEdgeTraits' for bsl::pair<T, T>. // TYPES typedef bsl::pair<NODE_TYPE, NODE_TYPE> EdgeType; // The type of the directed connection from one node to another typedef NODE_TYPE NodeType; // The type of values of the nodes of the directed acyclic graph // CLASS METHODS static const NODE_TYPE& from(const EdgeType& edge); // Return a 'const' reference to the 'from' attribute of the specified // 'edge' object. static const NODE_TYPE& to(const EdgeType& edge); // Return a 'to' reference to the 'from' attribute of the specified // 'edge' object. }; // ================================ // class TopologicalSortUtil_Helper // ================================ template <class INPUT_ITER> class TopologicalSortUtil_Helper { // This class template provides data structures required to sort the // partial unsorted edges into a total ordered set of nodes. The value // type of the nodes(*) must be hashable and equality comparable by // 'bsl::hash', and 'bsl::equal_to' respectively. // // (*) The value type is determined by first getting the iterator's value // type using 'bsl::iterator_traits<INPUT_ITER>::ValueType' and then // using 'TopologicalSortUtilEdgeTraits::ValueType' on that result. private: // PRIVATE TYPES typedef bsl::iterator_traits<INPUT_ITER> InIterTraits; typedef typename InIterTraits::value_type EdgeType; typedef TopologicalSortUtilEdgeTraits<EdgeType> EdgeTraits; typedef typename EdgeTraits::NodeType NodeType; // Get the traits type that corresponds to the incoming edge type, then // the types from it, with short names. typedef bsl::vector<NodeType> List; typedef typename List::iterator ListIter; typedef typename List::const_iterator ListConstIter; // An ordered list of nodes and their iterators struct Links { // Relations information for a given node in the format necessary for // topological sorting. The attributes are: // //: * the predecessor count: //: How many need to be sorted before this node comes in order? // //: * the successor nodes: //: All the nodes that must come after this node in the order // PUBLIC DATA int d_predecessorCount; // How many are before us? List d_successors; // those that are after us // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(Links, bslma::UsesBslmaAllocator); // CREATORS explicit Links(bslma::Allocator *allocator = 0); // Create a 'Links' which holds empty predecessor and successor // information. Optionally, specify an 'allocator' for needed // memory. If 'allocator' is 0, use the globally supply default // allocator instead. Links(const Links& original, bslma::Allocator *allocator = 0); // Create a 'Links' object having the same attribute values as the // specified 'original' object. Optionally specify an 'allocator' // used to supply memory. If 'allocator' is 0, the currently // installed default allocator is used. }; typedef bsl::unordered_map<NodeType, Links> LinksMap; typedef typename LinksMap::iterator LinksMapIter; typedef typename LinksMap::const_iterator LinksMapConstIter; // Mapping of nodes to their collected relations-information typedef bsl::queue<LinksMapIter> Fifo; // FIFO queue of nodes to process. Note that it is safe to store // iterators into the work-set/mapping in the queue because // 'bsl::unordered_map' guarantees the stability of every iterator // after a delete, except the iterators pointing to the deleted entry // itself. private: // DATA LinksMap d_workSet; // mapping from nodes to their relations // information (predecessorCount, successors) Fifo d_readyNodes; // elements whose predecessors have all already // been sorted (or never had predecessors) as // iterators into the mapping ('d_workSet') private: // NOT IMPLEMENTED TopologicalSortUtil_Helper(const TopologicalSortUtil_Helper&); TopologicalSortUtil_Helper& operator=(const TopologicalSortUtil_Helper&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION( TopologicalSortUtil_Helper, bslma::UsesBslmaAllocator); // CREATORS explicit TopologicalSortUtil_Helper(INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, bslma::Allocator *allocator = 0); // Create a helper class that holds the different data structures // required to sort in topological order the directed acyclic graph // described by the specified 'relationsBegin' and 'relationsEnd'. // Optionally, specify an 'allocator' for needed memory. If // 'allocator' is 0, use the globally supply default allocator instead. // MANIPULATORS template <class OUTPUT_ITER> bool processNextNodeInOrder(OUTPUT_ITER *resultOutIter_p); // Write the next element in order that doesn't have any predecessors // into the specified 'resultOutIter_p' output iterator then increment // it and return 'true'. If there are still elements left but all of // them still have predecessors do nothing and return 'false'. The // behavior is undefined unless '!d_workSet.empty()'. template <class OUTPUT_ITER, class UNSORTED_OUT_ITER> bool sortImpl(OUTPUT_ITER resultOutIter, UNSORTED_OUT_ITER unsortedOutIter); // Sort the input elements provided during construction in topological // order and write the resulting linear ordered set into the specified // 'resultOutIter' and return 'true'. If the sort is unsuccessful (the // input is not an acyclic directed graph) write the nodes that have // not been sorted to the specified 'unsortedOutIter' output and return // 'false'. See 'TopologicalSortUtil::sort' "iterators overload" for // more detailed specification. }; // ===================== // class TopologicalSort // ===================== struct TopologicalSortUtil { // This 'struct' 'TopologicalSortUtil' provides a namespace for topological // sorting functions. public: // CLASS METHODS template <class INPUT_ITER, class OUTPUT_ITER, class UNSORTED_OUTPUT_ITER> static bool sort(INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, OUTPUT_ITER resultOutIter, UNSORTED_OUTPUT_ITER unsortedOutIter); // Sort the input elements in topological order and write the resulting // linear ordered set into the specified 'resultOutIter'. If the // sort is unsuccessful (the input is not an acyclic directed graph) // write the elements that have not been sorted to the specified // 'unsortedOutIter' output. The input elements are provided as a // sequence of (conceptual or physical) pairs between the specified // 'relationsBegin' and 'relationsEnd' of the form (U, V), where U and // V are nodes, and U precedes V in the output. Return 'true' on // success, and 'false' if the sort fails due to a cycle in the input. // The type 'bsl::iterator_traits<INPUT_ITER>::value_type' must either // be 'bsl' or 'std::pair' where the 'first_type' and 'second_type' are // the same as 'bsl::iterator_traits<OUTPUT_ITER>::value_type' or // 'TopologicalSortUtilEdgeTraits' must be specialized for the type, // i.e., the supplied 'bsl::iterator_traits<INPUT_ITER>::value_type' // must support the following syntax: //.. // typedef typename bsl::iterator_traits<INPUT_ITER>::value_type // IterValue; // typedef typename bsl::iterator_traits<RESULT_ITER>::value_type // ResultValue; // // typedef typename TopologicalSortUtilEdgeTraits<IterValue> Traits; // // typedef typename Traits::NodeType NodeType; // // for (INPUT_ITER it = relationshipPairsBegin; // it != relationshipPairsEnd; // ++it) { // // *result++ = Traits::from(*it); // U // *result++ = Traits::to(*it); // V // } //.. // Note that when the method returns 'false', 'resultOutIter' may // contain a subset of the elements in the right topological order, // essentially the elements that the routine was able to sort before // the cycle was discovered. In that case, the elements that the // routine was unable to sort were written to the 'unsortedOutIter' // output iterator, in an unspecified order. template <class NODE_TYPE> static bool sort( bsl::vector<NODE_TYPE> *result, bsl::vector<NODE_TYPE> *unsorted, const bsl::vector<bsl::pair<NODE_TYPE, NODE_TYPE> >& relations); // Sort the elements of 'NODE_TYPE' in topological order determined by // the specified 'relations' and load the resulting linear ordered set // to the specified 'result'. If the sort is unsuccessful, load the // elements that have not been ordered to the specified 'unsorted' // list. The input relations are provided as pairs of the form (U, V) // where U precedes V in the output. Return 'false' if sort fails due // to a cycle in the input else return 'true' if sort successful. Note // that even if the method returns 'false', 'result' may contain a // subset of elements in the right topological order, essentially // elements that the routine was able to sort before the cycle was // discovered and 'unsorted' will contain the elements that the // routine was unable to sort. }; // ============================================================================ // INLINE AND TEMPLATE FUNCTION DEFINITIONS // ============================================================================ // ----------------------------------- // class TopologicalSortUtilEdgeTraits // ----------------------------------- template <class NODE_TYPE> inline const NODE_TYPE& TopologicalSortUtilEdgeTraits<bsl::pair<NODE_TYPE, NODE_TYPE> >:: from(const EdgeType& edge) { return edge.first; } template <class NODE_TYPE> inline const NODE_TYPE& TopologicalSortUtilEdgeTraits<bsl::pair<NODE_TYPE, NODE_TYPE> >:: to(const EdgeType& edge) { return edge.second; } // ------------------------------------------ // class TopologicalSortUtil_Helper::NodeInfo // ------------------------------------------ // CREATORS template <class INPUT_ITER> inline TopologicalSortUtil_Helper<INPUT_ITER>::Links::Links( bslma::Allocator *allocator) : d_predecessorCount(0) , d_successors(allocator) { } template <class INPUT_ITER> inline TopologicalSortUtil_Helper<INPUT_ITER>::Links::Links( const Links& original, bslma::Allocator *allocator) : d_predecessorCount(original.d_predecessorCount) , d_successors(original.d_successors, allocator) { } // -------------------------------- // class TopologicalSortUtil_Helper // -------------------------------- // CREATORS template <class INPUT_ITER> TopologicalSortUtil_Helper<INPUT_ITER>::TopologicalSortUtil_Helper( INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, bslma::Allocator *allocator) : d_workSet(allocator) , d_readyNodes(allocator) { // Iterate through the input list of edges and iteratively fill in the // relations information needed for sorting. for (INPUT_ITER iter = relationsBegin; iter != relationsEnd; ++iter) { const NodeType& from = EdgeTraits::from(*iter); const NodeType& to = EdgeTraits::to(*iter); ++d_workSet[to].d_predecessorCount; d_workSet[from].d_successors.push_back(to); } // Now iterate through the set and find nodes that are roots, i.e. which // don't have any predecessors and hence are the first in order. We put // these nodes into a queue that is ready to be written to the result. const LinksMapIter end = d_workSet.end(); for (LinksMapIter iter = d_workSet.begin(); iter != end; ++iter) { if (0 == iter->second.d_predecessorCount) { d_readyNodes.push(iter); } } } // MANIPULATORS template <class INPUT_ITER> template <class OUTPUT_ITER> bool TopologicalSortUtil_Helper<INPUT_ITER>::processNextNodeInOrder( OUTPUT_ITER *resultOutIter_p) { if (d_readyNodes.empty()) { // If the queue is empty but the input set is not then we have at least // one cycle. We know *here* that the input set is not empty because // it is a precondition for calling this function. return false; // RETURN } // process the next element (in 'd_readyNodes' FIFO) with no predecessors const LinksMapIter mapIter = d_readyNodes.front(); // move the processed node to the sorted output *(*resultOutIter_p) = mapIter->first; ++(*resultOutIter_p); // output iterator must be moved after a write // Iterate through the successor list of the node and reduce predecessor // count of each successor. Further, if that count becomes zero add that // successor to 'd_readyNodes'. for (ListIter successorIter = mapIter->second.d_successors.begin(); successorIter != mapIter->second.d_successors.end(); ++successorIter) { LinksMapIter succMapIter = d_workSet.find((*successorIter)); Links& succLinks = succMapIter->second; // One less predecessor now. if (--(succLinks.d_predecessorCount) == 0) { // No more predecessors? // This successor node is ready. d_readyNodes.push(succMapIter); } } d_readyNodes.pop(); // Drop the map iterator from the FIFO. d_workSet.erase(mapIter); // Remove the processed node from the work set. return true; } template <class INPUT_ITER> template <class OUTPUT_ITER, class UNSORTED_OUT_ITER> bool TopologicalSortUtil_Helper<INPUT_ITER>::sortImpl( OUTPUT_ITER resultOutIter, UNSORTED_OUT_ITER unsortedOutIter) { while (!d_workSet.empty()) { if (!processNextNodeInOrder(&resultOutIter)) { // A cycle was detected. Write all the nodes still present in the // work set to 'unsortedOutIter'. The nodes in the work set may be // in more than one cycle, or interconnected cycles, but our // algorithm ensures that all nodes left here *are* part of at // least one cycle. const LinksMapConstIter end = d_workSet.end(); for (LinksMapConstIter i = d_workSet.begin(); i != end; ++i) { *unsortedOutIter = i->first; ++unsortedOutIter; } return false; // RETURN } } return true; } // ------------------------- // class TopologicalSortUtil // ------------------------- // MANIPULATORS template <class INPUT_ITER, class OUTPUT_ITER, class UNSORTED_OUT_ITER> bool TopologicalSortUtil::sort(INPUT_ITER relationsBegin, INPUT_ITER relationsEnd, OUTPUT_ITER resultOutIter, UNSORTED_OUT_ITER unsortedOutIter) { typedef TopologicalSortUtil_Helper<INPUT_ITER> MyHelper; return MyHelper(relationsBegin, relationsEnd).sortImpl(resultOutIter, unsortedOutIter); } template <class NODE_TYPE> bool TopologicalSortUtil::sort( bsl::vector<NODE_TYPE> *result, bsl::vector<NODE_TYPE> *unsorted, const bsl::vector<bsl::pair<NODE_TYPE, NODE_TYPE> >& relations) { typedef bsl::vector<NODE_TYPE> vector_type; return sort(relations.begin(), relations.end(), bsl::back_insert_iterator<vector_type>(*result), bsl::back_insert_iterator<vector_type>(*unsorted)); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2018 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------