github.com/charypar/monobuild@v0.0.0-20211122220434-fd884ed50212/graph/graph.go (about)

     1  package graph
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/charypar/monobuild/set"
     7  )
     8  
     9  // Graph is a DAG with string labeled vertices and int colored edges
    10  type Graph struct {
    11  	edges map[string]Edges
    12  }
    13  
    14  // New creates a new Graph from a map of the shape
    15  // string: Edge
    16  // where Edge is a struct with a Label and a Colour
    17  func New(graph map[string][]Edge) Graph {
    18  	g := make(map[string]Edges)
    19  
    20  	// Copy and normalise the graph - every node needs a key in the map
    21  	for v, es := range graph {
    22  		g[v] = make(Edges, 0, len(es))
    23  
    24  		for _, e := range es {
    25  			g[v] = append(g[v], e)
    26  
    27  			_, ok := graph[e.Label]
    28  			if !ok {
    29  				g[e.Label] = Edges{}
    30  			}
    31  		}
    32  	}
    33  
    34  	return Graph{g}
    35  }
    36  
    37  // Vertices returns a full list of vertices in the graph
    38  func (g Graph) Vertices() []string {
    39  	vs := make([]string, 0, len(g.edges))
    40  
    41  	for v := range g.edges {
    42  		vs = append(vs, v)
    43  	}
    44  
    45  	sort.Strings(vs)
    46  	return vs
    47  }
    48  
    49  // Children returns the vertices that are connected to given vertices with an edge
    50  func (g Graph) Children(vertices []string) []string {
    51  	all := set.New([]string{})
    52  
    53  	for _, vertex := range vertices {
    54  		grandchildren, found := g.edges[vertex]
    55  		if !found {
    56  			continue
    57  		}
    58  
    59  		for _, gc := range grandchildren {
    60  			all.Add(gc.Label)
    61  		}
    62  	}
    63  
    64  	result := all.AsStrings()
    65  	sort.Strings(result)
    66  
    67  	return result
    68  }
    69  
    70  // Descendants returns all the vertices x for which a path to x exists from any of
    71  // the vertices given
    72  func (g Graph) Descendants(vertices []string) []string {
    73  	descendants := set.New(g.Children(vertices))
    74  	discovered := descendants
    75  
    76  	for discovered.Size() > 0 {
    77  		grandchildren := set.New(g.Children(discovered.AsStrings()))
    78  
    79  		discovered = grandchildren.Without(descendants)
    80  		descendants = descendants.Union(discovered)
    81  	}
    82  
    83  	result := descendants.AsStrings()
    84  	sort.Strings(result)
    85  
    86  	return result
    87  }
    88  
    89  // Reverse returns a new graph with edges reversed
    90  func (g Graph) Reverse() Graph {
    91  	edges := make(map[string]Edges)
    92  
    93  	// loop over the map keys deterministically
    94  	sorted := make([]string, 0, len(g.edges))
    95  	for v := range g.edges {
    96  		sorted = append(sorted, v)
    97  	}
    98  	sort.Strings(sorted)
    99  
   100  	// here
   101  	for _, v := range sorted {
   102  		_, ok := edges[v]
   103  		if !ok {
   104  			edges[v] = Edges{}
   105  		}
   106  
   107  		for _, e := range g.edges[v] {
   108  			_, ok := edges[e.Label]
   109  			if !ok {
   110  				edges[e.Label] = Edges{}
   111  			}
   112  
   113  			edges[e.Label] = append(edges[e.Label], Edge{v, e.Colour})
   114  		}
   115  	}
   116  
   117  	return Graph{edges}
   118  }
   119  
   120  // Subgraph filters the graph to only the nodes listed
   121  func (g Graph) Subgraph(nodes []string) Graph {
   122  	filter := set.New(nodes)
   123  	filtered := make(map[string]Edges, len(g.edges))
   124  
   125  	for v, es := range g.edges {
   126  		if !filter.Has(v) {
   127  			continue
   128  		}
   129  
   130  		filtered[v] = make(Edges, 0, len(es))
   131  		for _, e := range es {
   132  			if !filter.Has(e.Label) {
   133  				continue
   134  			}
   135  
   136  			filtered[v] = append(filtered[v], e)
   137  		}
   138  	}
   139  
   140  	return Graph{filtered}
   141  }
   142  
   143  // FilterEdges returns a new graph with edges with a colour not present in
   144  // colours removed
   145  func (g Graph) FilterEdges(colours []int) Graph {
   146  	filter := make(map[int]bool, len(colours))
   147  	for _, c := range colours {
   148  		filter[c] = true
   149  	}
   150  
   151  	filtered := make(map[string]Edges, len(g.edges))
   152  
   153  	for v, es := range g.edges {
   154  		_, ok := filtered[v]
   155  		if !ok {
   156  			filtered[v] = make([]Edge, 0, len(es))
   157  		}
   158  
   159  		for _, e := range es {
   160  			_, matches := filter[e.Colour]
   161  			if matches {
   162  				filtered[v] = append(filtered[v], e)
   163  			}
   164  		}
   165  	}
   166  
   167  	return Graph{filtered}
   168  }