LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - directedacyclicgraph.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 102 115 88.7 %
Date: 2024-05-03 15:49:35 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Implementation of topologic sorting over a directed acyclic graph
       5             :  * Author:   Even Rouault
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #ifndef DIRECTEDACYCLICGRAPH_INCLUDED_H
      30             : #define DIRECTEDACYCLICGRAPH_INCLUDED_H
      31             : 
      32             : #include <algorithm>
      33             : #include <list>
      34             : #include <map>
      35             : #include <set>
      36             : #include <stack>
      37             : #include <string>
      38             : #include <vector>
      39             : 
      40             : #include <cassert>
      41             : 
      42             : namespace gdal
      43             : {
      44             : 
      45             : // See https://en.wikipedia.org/wiki/Directed_acyclic_graph
      46             : template <class T, class V = std::string> class DirectedAcyclicGraph
      47             : {
      48             :     std::set<T> nodes{};
      49             :     std::map<T, std::set<T>>
      50             :         incomingNodes{};  // incomingNodes[j][i] means an edge from i to j
      51             :     std::map<T, std::set<T>>
      52             :         outgoingNodes{};  // outgoingNodes[i][j] means an edge from i to j
      53             :     std::map<T, V> names{};
      54             : 
      55             :   public:
      56        1251 :     DirectedAcyclicGraph() = default;
      57             : 
      58             :     void clear()
      59             :     {
      60             :         nodes.clear();
      61             :         incomingNodes.clear();
      62             :         outgoingNodes.clear();
      63             :         names.clear();
      64             :     }
      65             : 
      66        4670 :     void addNode(const T &i, const V &s)
      67             :     {
      68        4670 :         nodes.insert(i);
      69        4670 :         names[i] = s;
      70        4670 :     }
      71             : 
      72             :     void removeNode(const T &i);
      73             :     const char *addEdge(const T &i, const T &j);
      74             :     const char *removeEdge(const T &i, const T &j);
      75             :     bool isTherePathFromTo(const T &i, const T &j) const;
      76             :     std::vector<T> findStartingNodes() const;
      77             :     std::vector<T> getTopologicalOrdering();
      78             : };
      79             : 
      80             : template <class T, class V>
      81           2 : void DirectedAcyclicGraph<T, V>::removeNode(const T &i)
      82             : {
      83           2 :     nodes.erase(i);
      84           2 :     names.erase(i);
      85             : 
      86             :     {
      87           2 :         auto incomingIter = incomingNodes.find(i);
      88           2 :         if (incomingIter != incomingNodes.end())
      89             :         {
      90           4 :             for (const T &j : incomingIter->second)
      91             :             {
      92           2 :                 auto outgoingIter = outgoingNodes.find(j);
      93           2 :                 assert(outgoingIter != outgoingNodes.end());
      94           2 :                 auto iterJI = outgoingIter->second.find(i);
      95           2 :                 assert(iterJI != outgoingIter->second.end());
      96           2 :                 outgoingIter->second.erase(iterJI);
      97           2 :                 if (outgoingIter->second.empty())
      98           2 :                     outgoingNodes.erase(outgoingIter);
      99             :             }
     100           2 :             incomingNodes.erase(incomingIter);
     101             :         }
     102             :     }
     103             : 
     104             :     {
     105           2 :         auto outgoingIter = outgoingNodes.find(i);
     106           2 :         if (outgoingIter != outgoingNodes.end())
     107             :         {
     108           0 :             for (const T &j : outgoingIter->second)
     109             :             {
     110           0 :                 auto incomingIter = incomingNodes.find(j);
     111           0 :                 assert(incomingIter != incomingNodes.end());
     112           0 :                 auto iterJI = incomingIter->second.find(i);
     113           0 :                 assert(iterJI != incomingIter->second.end());
     114           0 :                 incomingIter->second.erase(iterJI);
     115           0 :                 if (incomingIter->second.empty())
     116           0 :                     incomingNodes.erase(incomingIter);
     117             :             }
     118           0 :             outgoingNodes.erase(outgoingIter);
     119             :         }
     120             :     }
     121           2 : }
     122             : 
     123             : template <class T, class V>
     124        2251 : const char *DirectedAcyclicGraph<T, V>::addEdge(const T &i, const T &j)
     125             : {
     126        2251 :     if (i == j)
     127             :     {
     128           2 :         return "self cycle";
     129             :     }
     130        2249 :     const auto iterI = outgoingNodes.find(i);
     131        3821 :     if (iterI != outgoingNodes.end() &&
     132        3821 :         iterI->second.find(j) != iterI->second.end())
     133             :     {
     134        1510 :         return "already inserted edge";
     135             :     }
     136             : 
     137         739 :     if (nodes.find(i) == nodes.end())
     138             :     {
     139           0 :         return "node i unknown";
     140             :     }
     141         739 :     if (nodes.find(j) == nodes.end())
     142             :     {
     143           0 :         return "node j unknown";
     144             :     }
     145             : 
     146         739 :     if (isTherePathFromTo(j, i))
     147             :     {
     148          40 :         return "can't add edge: this would cause a cycle";
     149             :     }
     150             : 
     151         699 :     outgoingNodes[i].insert(j);
     152         699 :     incomingNodes[j].insert(i);
     153         699 :     return nullptr;
     154             : }
     155             : 
     156             : template <class T, class V>
     157         697 : const char *DirectedAcyclicGraph<T, V>::removeEdge(const T &i, const T &j)
     158             : {
     159         697 :     auto iterI = outgoingNodes.find(i);
     160         697 :     if (iterI == outgoingNodes.end())
     161           0 :         return "no outgoing nodes from i";
     162         697 :     auto iterIJ = iterI->second.find(j);
     163         697 :     if (iterIJ == iterI->second.end())
     164           0 :         return "no outgoing node from i to j";
     165         697 :     iterI->second.erase(iterIJ);
     166         697 :     if (iterI->second.empty())
     167         672 :         outgoingNodes.erase(iterI);
     168             : 
     169         697 :     auto iterJ = incomingNodes.find(j);
     170         697 :     assert(iterJ != incomingNodes.end());
     171         697 :     auto iterJI = iterJ->second.find(i);
     172         697 :     assert(iterJI != iterJ->second.end());
     173         697 :     iterJ->second.erase(iterJI);
     174         697 :     if (iterJ->second.empty())
     175         673 :         incomingNodes.erase(iterJ);
     176             : 
     177         697 :     return nullptr;
     178             : }
     179             : 
     180             : template <class T, class V>
     181         739 : bool DirectedAcyclicGraph<T, V>::isTherePathFromTo(const T &i, const T &j) const
     182             : {
     183        1478 :     std::set<T> plannedForVisit;
     184        1478 :     std::stack<T> toVisit;
     185         739 :     toVisit.push(i);
     186         739 :     plannedForVisit.insert(i);
     187        1612 :     while (!toVisit.empty())
     188             :     {
     189         913 :         const T n = toVisit.top();
     190         913 :         toVisit.pop();
     191         913 :         if (n == j)
     192          40 :             return true;
     193         873 :         const auto iter = outgoingNodes.find(n);
     194         873 :         if (iter != outgoingNodes.end())
     195             :         {
     196         384 :             for (const T &k : iter->second)
     197             :             {
     198         211 :                 if (plannedForVisit.find(k) == plannedForVisit.end())
     199             :                 {
     200         175 :                     plannedForVisit.insert(k);
     201         175 :                     toVisit.push(k);
     202             :                 }
     203             :             }
     204             :         }
     205             :     }
     206         699 :     return false;
     207             : }
     208             : 
     209             : template <class T, class V>
     210         612 : std::vector<T> DirectedAcyclicGraph<T, V>::findStartingNodes() const
     211             : {
     212         612 :     std::vector<T> ret;
     213        1622 :     for (const auto &i : nodes)
     214             :     {
     215        1010 :         if (incomingNodes.find(i) == incomingNodes.end())
     216         337 :             ret.emplace_back(i);
     217             :     }
     218         612 :     return ret;
     219             : }
     220             : 
     221             : // Kahn's algorithm:
     222             : // https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
     223             : template <class T, class V>
     224         612 : std::vector<T> DirectedAcyclicGraph<T, V>::getTopologicalOrdering()
     225             : {
     226         612 :     std::vector<T> ret;
     227         612 :     ret.reserve(nodes.size());
     228             : 
     229         722 :     const auto cmp = [this](const T &a, const T &b)
     230          55 :     { return names.find(a)->second < names.find(b)->second; };
     231        1224 :     std::set<T, decltype(cmp)> S(cmp);
     232             : 
     233        1224 :     const auto sn = findStartingNodes();
     234         949 :     for (const auto &i : sn)
     235         337 :         S.insert(i);
     236             : 
     237        1010 :     while (true)
     238             :     {
     239        1622 :         auto iterFirst = S.begin();
     240        1622 :         if (iterFirst == S.end())
     241         612 :             break;
     242        1010 :         const auto n = *iterFirst;
     243        1010 :         S.erase(iterFirst);
     244        1010 :         ret.emplace_back(n);
     245             : 
     246        1010 :         const auto iter = outgoingNodes.find(n);
     247        1010 :         if (iter != outgoingNodes.end())
     248             :         {
     249             :             // Need to take a copy as we remove edges during iteration
     250        1344 :             const std::set<T> myOutgoingNodes = iter->second;
     251        1369 :             for (const T &m : myOutgoingNodes)
     252             :             {
     253         697 :                 const char *retRemoveEdge = removeEdge(n, m);
     254             :                 (void)retRemoveEdge;
     255         697 :                 assert(retRemoveEdge == nullptr);
     256         697 :                 if (incomingNodes.find(m) == incomingNodes.end())
     257             :                 {
     258         673 :                     S.insert(m);
     259             :                 }
     260             :             }
     261             :         }
     262             :     }
     263             : 
     264             :     // Should not happen for a direct acyclic graph
     265         612 :     assert(incomingNodes.empty());
     266         612 :     assert(outgoingNodes.empty());
     267             : 
     268        1224 :     return ret;
     269             : }
     270             : 
     271             : }  // namespace gdal
     272             : 
     273             : #endif  // DIRECTEDACYCLICGRAPH_INCLUDED_H

Generated by: LCOV version 1.14