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