/* * Copyright 2014 Google Inc. All rights reserved. * * 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. */ #ifndef FRUIT_META_GRAPH_H #define FRUIT_META_GRAPH_H #include #include #include #include namespace fruit { namespace impl { namespace meta { // A Graph is a Map from each node to a set containing its neighbors. using GetGraphNodes = GetImmutableMapKeys; using GraphFindNeighbors = FindInImmutableMap; using GraphContainsNode = ImmutableMapContainsKey; // Returns a loop in the given graph as a Vector such that the graph contains a loop // N1->...->Nk->N1, or None if there are no loops. struct GraphFindLoop { template struct apply { using ImmutableG = VectorToImmutableMap(G); // DfsVisit(VisitedSet, VisitingSet, Node) does a DFS visit starting at Node and returns a // Triplet, where Loop is the Vector representing the part // of the loop starting at Node (if any loop was found) or Null otherwise. struct DfsVisit { template struct apply { using NewVisitingSet = AddToSetUnchecked(VisitingSet, Node); struct VisitSingleNeighbor { // CurrentResult is a Triplet (where IsLoopComplete // is only meaningful when Loop is not None). template struct apply { using type = If(IsNone(typename CurrentResult::Second), // Go ahead, no loop found yet. DfsVisit(typename CurrentResult::First, NewVisitingSet, Neighbor), // Found a loop in another neighbor of the same node, we don't need to // visit this neighbor. CurrentResult); }; }; using NewVisitedSet = AddToSet(VisitedSet, Node); using Neighbors = GraphFindNeighbors(ImmutableG, Node); using Result = FoldVector(Neighbors, VisitSingleNeighbor, ConsTriplet(NewVisitedSet, None, Bool)); using type = If(IsNone(Neighbors), // No neighbors. ConsTriplet(NewVisitedSet, None, Bool), If(IsInSet(Node, VisitingSet), // We've just found a loop, since Node is another node that we're currently // visiting Triplet, Bool>, If(IsNone(GetSecond(Result)), // No loop found. Result, // Found a loop If(GetThird(Result) /* IsLoopComplete */, Result, If(VectorEndsWith(GetSecond(Result) /* Loop */, Node), // The loop is complete now. ConsTriplet(GetFirst(Result), GetSecond(Result), Bool), // Loop still not complete, add the current node. ConsTriplet(GetFirst(Result), PushFront(GetSecond(Result), Node), Bool)))))); }; }; struct VisitStartingAtNode { // CurrentResult is a Pair template struct apply { using DfsResult = DfsVisit(GetFirst(CurrentResult), EmptySet, Node); using type = If(IsNone(GetSecond(CurrentResult)), // No loop found yet. MakePair(GetFirst(DfsResult), GetSecond(DfsResult)), // Found a loop, return early CurrentResult); }; }; using type = GetSecond(FoldVector(GetMapKeys(G), VisitStartingAtNode, Pair)); }; }; } // namespace meta } // namespace impl } // namespace fruit #endif // FRUIT_META_GRAPH_H