14d6c458bSopenharmony_ci/* 24d6c458bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd. 34d6c458bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 44d6c458bSopenharmony_ci * you may not use this file except in compliance with the License. 54d6c458bSopenharmony_ci * You may obtain a copy of the License at 64d6c458bSopenharmony_ci * 74d6c458bSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 84d6c458bSopenharmony_ci * 94d6c458bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 104d6c458bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 114d6c458bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 124d6c458bSopenharmony_ci * See the License for the specific language governing permissions and 134d6c458bSopenharmony_ci * limitations under the License. 144d6c458bSopenharmony_ci */ 154d6c458bSopenharmony_ci 164d6c458bSopenharmony_ci#ifndef JS_CONCURRENT_MODULE_UTILS_LOCKS_GRAPH_H 174d6c458bSopenharmony_ci#define JS_CONCURRENT_MODULE_UTILS_LOCKS_GRAPH_H 184d6c458bSopenharmony_ci 194d6c458bSopenharmony_ci#include <vector> 204d6c458bSopenharmony_ci#include <map> 214d6c458bSopenharmony_ci#include <string> 224d6c458bSopenharmony_ci#include <limits> 234d6c458bSopenharmony_ci#include <stack> 244d6c458bSopenharmony_ci#include <functional> 254d6c458bSopenharmony_ci 264d6c458bSopenharmony_cinamespace Commonlibrary::Concurrent::LocksModule { 274d6c458bSopenharmony_ci 284d6c458bSopenharmony_citemplate <class VertexId = size_t, class EdgeData = void> 294d6c458bSopenharmony_ciclass Graph { 304d6c458bSopenharmony_cipublic: 314d6c458bSopenharmony_ci using EdgeDataCPtr = const EdgeData *; 324d6c458bSopenharmony_ci 334d6c458bSopenharmony_ciprivate: 344d6c458bSopenharmony_ci struct Vertex { 354d6c458bSopenharmony_ci enum class Color { WHITE = 0, GREY = 1, BLACK = 2 }; 364d6c458bSopenharmony_ci Color color = Color::WHITE; 374d6c458bSopenharmony_ci VertexId data = VertexId {}; 384d6c458bSopenharmony_ci }; 394d6c458bSopenharmony_ci using Vertices = std::vector<Vertex>; 404d6c458bSopenharmony_ci using VIdx = size_t; 414d6c458bSopenharmony_ci using VColor = typename Vertex::Color; 424d6c458bSopenharmony_ci using AdjacencyMatrix = std::vector<std::vector<EdgeDataCPtr>>; 434d6c458bSopenharmony_ci 444d6c458bSopenharmony_ci static constexpr VIdx INVALID_V_IDX = std::numeric_limits<VIdx>::max(); 454d6c458bSopenharmony_ci 464d6c458bSopenharmony_cipublic: 474d6c458bSopenharmony_ci // from, to, what 484d6c458bSopenharmony_ci using EdgeDef = std::tuple<VertexId, VertexId, EdgeDataCPtr>; 494d6c458bSopenharmony_ci using AdjacencyList = std::vector<EdgeDef>; 504d6c458bSopenharmony_ci // for pretty printing paths 514d6c458bSopenharmony_ci using VertexPrinter = std::function<std::string(VertexId)>; 524d6c458bSopenharmony_ci using EdgePrinter = std::function<std::string(EdgeDataCPtr)>; 534d6c458bSopenharmony_ci 544d6c458bSopenharmony_ci struct Path { 554d6c458bSopenharmony_ci // vertex0 -> edge0 -> vertex1 -> edge1 -> ... 564d6c458bSopenharmony_ci std::vector<VertexId> vertices; 574d6c458bSopenharmony_ci std::vector<EdgeDataCPtr> edges; 584d6c458bSopenharmony_ci bool IsEmpty() 594d6c458bSopenharmony_ci { 604d6c458bSopenharmony_ci return vertices.empty(); 614d6c458bSopenharmony_ci } 624d6c458bSopenharmony_ci }; 634d6c458bSopenharmony_ci 644d6c458bSopenharmony_ci static constexpr auto defaultVertexPrinter = [](VertexId vid) { return std::to_string(vid); }; 654d6c458bSopenharmony_ci static constexpr auto DEFAULT_EDGE_PRINTER = []() { return " <- "; }; 664d6c458bSopenharmony_ci 674d6c458bSopenharmony_ci explicit Graph(AdjacencyList &&adj) 684d6c458bSopenharmony_ci { 694d6c458bSopenharmony_ci std::map<VertexId, size_t> vDataPtrToVidx; 704d6c458bSopenharmony_ci size_t size = v_.size() > 0 ? v_.size() - 1 : 0; 714d6c458bSopenharmony_ci for (auto &edef : adj) { 724d6c458bSopenharmony_ci // from 734d6c458bSopenharmony_ci auto fromVertexPtr = std::get<0>(edef); 744d6c458bSopenharmony_ci if (vDataPtrToVidx.find(fromVertexPtr) == vDataPtrToVidx.end()) { 754d6c458bSopenharmony_ci v_.push_back({VColor::WHITE, fromVertexPtr}); 764d6c458bSopenharmony_ci vDataPtrToVidx[fromVertexPtr] = size; 774d6c458bSopenharmony_ci } 784d6c458bSopenharmony_ci // to 794d6c458bSopenharmony_ci auto toVertexPtr = std::get<1>(edef); 804d6c458bSopenharmony_ci if (vDataPtrToVidx.find(toVertexPtr) == vDataPtrToVidx.end()) { 814d6c458bSopenharmony_ci v_.push_back({VColor::WHITE, toVertexPtr}); 824d6c458bSopenharmony_ci vDataPtrToVidx[toVertexPtr] = size; 834d6c458bSopenharmony_ci } 844d6c458bSopenharmony_ci } 854d6c458bSopenharmony_ci // For now let's use an adjacency matrix as the storage. 864d6c458bSopenharmony_ci // Will use an adjacency list later on. 874d6c458bSopenharmony_ci e_.resize(v_.size()); 884d6c458bSopenharmony_ci for (auto &row : e_) { 894d6c458bSopenharmony_ci row.resize(v_.size()); 904d6c458bSopenharmony_ci for (auto &eid : row) { 914d6c458bSopenharmony_ci eid = nullptr; 924d6c458bSopenharmony_ci } 934d6c458bSopenharmony_ci } 944d6c458bSopenharmony_ci for (auto &edef : adj) { 954d6c458bSopenharmony_ci auto fromVertexPtr = std::get<0>(edef); 964d6c458bSopenharmony_ci auto toVertexPtr = std::get<1>(edef); 974d6c458bSopenharmony_ci auto edgeDataPtr = std::get<2>(edef); 984d6c458bSopenharmony_ci e_[vDataPtrToVidx[fromVertexPtr]][vDataPtrToVidx[toVertexPtr]] = edgeDataPtr; 994d6c458bSopenharmony_ci } 1004d6c458bSopenharmony_ci } 1014d6c458bSopenharmony_ci 1024d6c458bSopenharmony_ci bool IsValid() 1034d6c458bSopenharmony_ci { 1044d6c458bSopenharmony_ci // NOTE: maybe its worth to add more detailed verification 1054d6c458bSopenharmony_ci size_t n = e_.size(); 1064d6c458bSopenharmony_ci if (e_.empty()) { 1074d6c458bSopenharmony_ci return false; 1084d6c458bSopenharmony_ci } 1094d6c458bSopenharmony_ci for (auto &row : e_) { 1104d6c458bSopenharmony_ci if (row.size() != n) { 1114d6c458bSopenharmony_ci return false; 1124d6c458bSopenharmony_ci } 1134d6c458bSopenharmony_ci } 1144d6c458bSopenharmony_ci return true; 1154d6c458bSopenharmony_ci } 1164d6c458bSopenharmony_ci 1174d6c458bSopenharmony_ci static std::string CycleAsString(Path cycle, std::string prompt = "L: ", std::string terminator = "|", 1184d6c458bSopenharmony_ci VertexPrinter vertexPrinter = defaultVertexPrinter, 1194d6c458bSopenharmony_ci EdgePrinter edgePrinter = DEFAULT_EDGE_PRINTER) 1204d6c458bSopenharmony_ci { 1214d6c458bSopenharmony_ci if (cycle.vertices.empty() || cycle.vertices.size() < 1 || 1224d6c458bSopenharmony_ci (cycle.edges.size() != (cycle.vertices.size() - 1))) { 1234d6c458bSopenharmony_ci return ""; 1244d6c458bSopenharmony_ci } 1254d6c458bSopenharmony_ci std::stringstream result; 1264d6c458bSopenharmony_ci result << prompt; 1274d6c458bSopenharmony_ci auto edgeIt = cycle.edges.begin(); 1284d6c458bSopenharmony_ci for (auto vDataPtr : cycle.vertices) { 1294d6c458bSopenharmony_ci result << vertexPrinter(vDataPtr); 1304d6c458bSopenharmony_ci if (edgeIt != cycle.edges.end()) { 1314d6c458bSopenharmony_ci result << edgePrinter(*edgeIt); 1324d6c458bSopenharmony_ci ++edgeIt; 1334d6c458bSopenharmony_ci } 1344d6c458bSopenharmony_ci } 1354d6c458bSopenharmony_ci result << terminator; 1364d6c458bSopenharmony_ci return result.str(); 1374d6c458bSopenharmony_ci } 1384d6c458bSopenharmony_ci 1394d6c458bSopenharmony_ci Path FindFirstCycle() 1404d6c458bSopenharmony_ci { 1414d6c458bSopenharmony_ci // verify 1424d6c458bSopenharmony_ci if (!IsValid()) { 1434d6c458bSopenharmony_ci return Path {}; 1444d6c458bSopenharmony_ci } 1454d6c458bSopenharmony_ci // run dfs 1464d6c458bSopenharmony_ci for (VIdx seedIdx = 0; seedIdx < NumVertices(); ++seedIdx) { 1474d6c458bSopenharmony_ci if (VertexColor(seedIdx) != Graph::VColor::WHITE) { 1484d6c458bSopenharmony_ci continue; 1494d6c458bSopenharmony_ci } 1504d6c458bSopenharmony_ci Path cycle = RunDfsFromVertex(seedIdx); 1514d6c458bSopenharmony_ci if (!cycle.vertices.empty()) { 1524d6c458bSopenharmony_ci return cycle; 1534d6c458bSopenharmony_ci } 1544d6c458bSopenharmony_ci } 1554d6c458bSopenharmony_ci return Path {}; 1564d6c458bSopenharmony_ci } 1574d6c458bSopenharmony_ci 1584d6c458bSopenharmony_ciprivate: 1594d6c458bSopenharmony_ci VColor VertexColor(VIdx vidx) 1604d6c458bSopenharmony_ci { 1614d6c458bSopenharmony_ci return v_[vidx].color; 1624d6c458bSopenharmony_ci } 1634d6c458bSopenharmony_ci 1644d6c458bSopenharmony_ci bool HasEdge(VIdx fromIdx, VIdx toIdx) 1654d6c458bSopenharmony_ci { 1664d6c458bSopenharmony_ci return e_[fromIdx][toIdx] != nullptr; 1674d6c458bSopenharmony_ci } 1684d6c458bSopenharmony_ci 1694d6c458bSopenharmony_ci size_t NumVertices() 1704d6c458bSopenharmony_ci { 1714d6c458bSopenharmony_ci return v_.size(); 1724d6c458bSopenharmony_ci } 1734d6c458bSopenharmony_ci 1744d6c458bSopenharmony_ci void Mark(VIdx vidx, VColor color) 1754d6c458bSopenharmony_ci { 1764d6c458bSopenharmony_ci v_[vidx].color = color; 1774d6c458bSopenharmony_ci } 1784d6c458bSopenharmony_ci 1794d6c458bSopenharmony_ci enum class DfsAction { RESTART = 0, FINISH = 1, PROCEED = 2 }; 1804d6c458bSopenharmony_ci struct DfsState { 1814d6c458bSopenharmony_ci VIdx currentIdx; 1824d6c458bSopenharmony_ci VIdx childIdx; 1834d6c458bSopenharmony_ci }; 1844d6c458bSopenharmony_ci using DfsStack = std::stack<DfsState>; 1854d6c458bSopenharmony_ci 1864d6c458bSopenharmony_ci Path DfsBuildCycleInfo(DfsStack &dfsStack, DfsState state) 1874d6c458bSopenharmony_ci { 1884d6c458bSopenharmony_ci Path result; 1894d6c458bSopenharmony_ci VIdx originIdx = state.childIdx; 1904d6c458bSopenharmony_ci result.vertices.push_back(v_[state.childIdx].data); 1914d6c458bSopenharmony_ci result.vertices.push_back(v_[state.currentIdx].data); 1924d6c458bSopenharmony_ci result.edges.push_back(e_[state.currentIdx][state.childIdx]); 1934d6c458bSopenharmony_ci VIdx prevIdx = state.currentIdx; 1944d6c458bSopenharmony_ci while (!dfsStack.empty()) { 1954d6c458bSopenharmony_ci auto s = dfsStack.top(); 1964d6c458bSopenharmony_ci dfsStack.pop(); 1974d6c458bSopenharmony_ci result.vertices.push_back(v_[s.currentIdx].data); 1984d6c458bSopenharmony_ci result.edges.push_back(e_[s.currentIdx][prevIdx]); 1994d6c458bSopenharmony_ci prevIdx = s.currentIdx; 2004d6c458bSopenharmony_ci if (s.currentIdx == originIdx) { 2014d6c458bSopenharmony_ci break; 2024d6c458bSopenharmony_ci } 2034d6c458bSopenharmony_ci } 2044d6c458bSopenharmony_ci return result; 2054d6c458bSopenharmony_ci } 2064d6c458bSopenharmony_ci 2074d6c458bSopenharmony_ci DfsAction DfsVisitChildren(DfsStack &dfsStack, DfsState &state) 2084d6c458bSopenharmony_ci { 2094d6c458bSopenharmony_ci for (; state.childIdx < NumVertices(); ++state.childIdx) { 2104d6c458bSopenharmony_ci if (HasEdge(state.currentIdx, state.childIdx)) { 2114d6c458bSopenharmony_ci // the edge v -> child exists 2124d6c458bSopenharmony_ci if (VertexColor(state.childIdx) == Graph::VColor::BLACK) { 2134d6c458bSopenharmony_ci // the child processing is already completed 2144d6c458bSopenharmony_ci continue; 2154d6c458bSopenharmony_ci } 2164d6c458bSopenharmony_ci if (VertexColor(state.childIdx) == Graph::VColor::GREY) { 2174d6c458bSopenharmony_ci // the child is visited: means a cycle has been found 2184d6c458bSopenharmony_ci return DfsAction::FINISH; 2194d6c458bSopenharmony_ci } 2204d6c458bSopenharmony_ci // the child is "white", i.e. not visited yet: run DFS from it 2214d6c458bSopenharmony_ci VIdx nextChild = state.childIdx + 1 < NumVertices() ? state.childIdx + 1 : Graph::INVALID_V_IDX; 2224d6c458bSopenharmony_ci dfsStack.push(DfsState {state.currentIdx, nextChild}); 2234d6c458bSopenharmony_ci state.currentIdx = state.childIdx; 2244d6c458bSopenharmony_ci state.childIdx = 0; 2254d6c458bSopenharmony_ci return DfsAction::RESTART; 2264d6c458bSopenharmony_ci } 2274d6c458bSopenharmony_ci } 2284d6c458bSopenharmony_ci state.childIdx = Graph::INVALID_V_IDX; 2294d6c458bSopenharmony_ci return DfsAction::PROCEED; 2304d6c458bSopenharmony_ci } 2314d6c458bSopenharmony_ci 2324d6c458bSopenharmony_ci bool DfsPopState(DfsStack &dfsStack, DfsState &state) 2334d6c458bSopenharmony_ci { 2344d6c458bSopenharmony_ci while (!dfsStack.empty()) { 2354d6c458bSopenharmony_ci state = dfsStack.top(); 2364d6c458bSopenharmony_ci dfsStack.pop(); 2374d6c458bSopenharmony_ci if (state.childIdx != Graph::INVALID_V_IDX) { 2384d6c458bSopenharmony_ci return true; 2394d6c458bSopenharmony_ci } 2404d6c458bSopenharmony_ci Mark(state.currentIdx, Graph::VColor::BLACK); 2414d6c458bSopenharmony_ci } 2424d6c458bSopenharmony_ci return false; 2434d6c458bSopenharmony_ci } 2444d6c458bSopenharmony_ci 2454d6c458bSopenharmony_ci Path RunDfsFromVertex(VIdx seedVertexIdx) 2464d6c458bSopenharmony_ci { 2474d6c458bSopenharmony_ci DfsStack dfsStack; 2484d6c458bSopenharmony_ci DfsState state {seedVertexIdx, 0}; 2494d6c458bSopenharmony_ci bool areUnprocessedVerticesLeft = true; 2504d6c458bSopenharmony_ci 2514d6c458bSopenharmony_ci while (areUnprocessedVerticesLeft) { 2524d6c458bSopenharmony_ci Mark(state.currentIdx, Graph::VColor::GREY); 2534d6c458bSopenharmony_ci DfsAction a = DfsVisitChildren(dfsStack, state); 2544d6c458bSopenharmony_ci switch (a) { 2554d6c458bSopenharmony_ci case DfsAction::FINISH: 2564d6c458bSopenharmony_ci return DfsBuildCycleInfo(dfsStack, state); 2574d6c458bSopenharmony_ci case DfsAction::RESTART: 2584d6c458bSopenharmony_ci continue; 2594d6c458bSopenharmony_ci case DfsAction::PROCEED: 2604d6c458bSopenharmony_ci default: 2614d6c458bSopenharmony_ci break; 2624d6c458bSopenharmony_ci } 2634d6c458bSopenharmony_ci Mark(state.currentIdx, Graph::VColor::BLACK); 2644d6c458bSopenharmony_ci areUnprocessedVerticesLeft = DfsPopState(dfsStack, state); 2654d6c458bSopenharmony_ci } 2664d6c458bSopenharmony_ci // no cycles found 2674d6c458bSopenharmony_ci return Path {}; 2684d6c458bSopenharmony_ci } 2694d6c458bSopenharmony_ci 2704d6c458bSopenharmony_ci Vertices v_; 2714d6c458bSopenharmony_ci AdjacencyMatrix e_; 2724d6c458bSopenharmony_ci}; 2734d6c458bSopenharmony_ci 2744d6c458bSopenharmony_ci} // namespace Commonlibrary::Concurrent::LocksModule 2754d6c458bSopenharmony_ci 2764d6c458bSopenharmony_ci#endif 277