dep.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package topo
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/Jguer/yay/v12/pkg/text"
  6. )
  7. type (
  8. NodeSet[T comparable] map[T]bool
  9. DepMap[T comparable] map[T]NodeSet[T]
  10. )
  11. func (n NodeSet[T]) Slice() []T {
  12. var slice []T
  13. for node := range n {
  14. slice = append(slice, node)
  15. }
  16. return slice
  17. }
  18. type NodeInfo[V any] struct {
  19. Color string
  20. Background string
  21. Value V
  22. }
  23. type CheckFn[T comparable, V any] func(T, V) error
  24. type Graph[T comparable, V any] struct {
  25. nodes NodeSet[T]
  26. // node info map
  27. nodeInfo map[T]*NodeInfo[V]
  28. // `dependencies` tracks child -> parents.
  29. dependencies DepMap[T]
  30. // `dependents` tracks parent -> children.
  31. dependents DepMap[T]
  32. }
  33. func New[T comparable, V any]() *Graph[T, V] {
  34. return &Graph[T, V]{
  35. nodes: make(NodeSet[T]),
  36. dependencies: make(DepMap[T]),
  37. dependents: make(DepMap[T]),
  38. nodeInfo: make(map[T]*NodeInfo[V]),
  39. }
  40. }
  41. func (g *Graph[T, V]) Len() int {
  42. return len(g.nodes)
  43. }
  44. func (g *Graph[T, V]) Exists(node T) bool {
  45. _, ok := g.nodes[node]
  46. return ok
  47. }
  48. func (g *Graph[T, V]) AddNode(node T) {
  49. g.nodes[node] = true
  50. }
  51. func (g *Graph[T, V]) ForEach(f CheckFn[T, V]) error {
  52. for node := range g.nodes {
  53. if err := f(node, g.nodeInfo[node].Value); err != nil {
  54. return err
  55. }
  56. }
  57. return nil
  58. }
  59. func (g *Graph[T, V]) SetNodeInfo(node T, nodeInfo *NodeInfo[V]) {
  60. g.nodeInfo[node] = nodeInfo
  61. }
  62. func (g *Graph[T, V]) GetNodeInfo(node T) *NodeInfo[V] {
  63. return g.nodeInfo[node]
  64. }
  65. func (g *Graph[T, V]) DependOn(child, parent T) error {
  66. if child == parent {
  67. return ErrSelfReferential
  68. }
  69. if g.DependsOn(parent, child) {
  70. return ErrCircular
  71. }
  72. g.AddNode(parent)
  73. g.AddNode(child)
  74. // Add nodes.
  75. g.nodes[parent] = true
  76. g.nodes[child] = true
  77. // Add edges.
  78. g.dependents.addNodeToNodeset(parent, child)
  79. g.dependencies.addNodeToNodeset(child, parent)
  80. return nil
  81. }
  82. func (g *Graph[T, V]) String() string {
  83. var sb strings.Builder
  84. sb.WriteString("digraph {\n")
  85. sb.WriteString("compound=true;\n")
  86. sb.WriteString("concentrate=true;\n")
  87. sb.WriteString("node [shape = record, ordering=out];\n")
  88. for node := range g.nodes {
  89. extra := ""
  90. if info, ok := g.nodeInfo[node]; ok {
  91. if info.Background != "" || info.Color != "" {
  92. extra = fmt.Sprintf("[color = %s, style = filled, fillcolor = %s]", info.Color, info.Background)
  93. }
  94. }
  95. sb.WriteString(fmt.Sprintf("\t\"%v\"%s;\n", node, extra))
  96. }
  97. for parent, children := range g.dependencies {
  98. for child := range children {
  99. sb.WriteString(fmt.Sprintf("\t\"%v\" -> \"%v\";\n", parent, child))
  100. }
  101. }
  102. sb.WriteString("}")
  103. return sb.String()
  104. }
  105. func (g *Graph[T, V]) DependsOn(child, parent T) bool {
  106. deps := g.Dependencies(child)
  107. _, ok := deps[parent]
  108. return ok
  109. }
  110. func (g *Graph[T, V]) HasDependent(parent, child T) bool {
  111. deps := g.Dependents(parent)
  112. _, ok := deps[child]
  113. return ok
  114. }
  115. // leavesMap returns a map of leaves with the node as key and the node info value as value.
  116. func (g *Graph[T, V]) leavesMap() map[T]V {
  117. leaves := make(map[T]V, 0)
  118. for node := range g.nodes {
  119. if _, ok := g.dependencies[node]; !ok {
  120. nodeInfo := g.GetNodeInfo(node)
  121. if nodeInfo == nil {
  122. nodeInfo = &NodeInfo[V]{}
  123. }
  124. leaves[node] = nodeInfo.Value
  125. }
  126. }
  127. return leaves
  128. }
  129. // TopoSortedLayerMap returns a slice of all of the graph nodes in topological sort order with their node info.
  130. func (g *Graph[T, V]) TopoSortedLayerMap(checkFn CheckFn[T, V]) []map[T]V {
  131. layers := []map[T]V{}
  132. // Copy the graph
  133. shrinkingGraph := g.clone()
  134. for {
  135. leaves := shrinkingGraph.leavesMap()
  136. if len(leaves) == 0 {
  137. break
  138. }
  139. layers = append(layers, leaves)
  140. for leafNode := range leaves {
  141. if checkFn != nil {
  142. if err := checkFn(leafNode, leaves[leafNode]); err != nil {
  143. return nil
  144. }
  145. }
  146. shrinkingGraph.remove(leafNode)
  147. }
  148. }
  149. return layers
  150. }
  151. // returns if it was the last
  152. func (dm DepMap[T]) removeFromDepmap(key, node T) bool {
  153. if nodes := dm[key]; len(nodes) == 1 {
  154. // The only element in the nodeset must be `node`, so we
  155. // can delete the entry entirely.
  156. delete(dm, key)
  157. return true
  158. } else {
  159. // Otherwise, remove the single node from the nodeset.
  160. delete(nodes, node)
  161. return false
  162. }
  163. }
  164. // Prune removes the node,
  165. // its dependencies if there are no other dependents
  166. // and its dependents
  167. func (g *Graph[T, V]) Prune(node T) []T {
  168. pruned := []T{node}
  169. // Remove edges from things that depend on `node`.
  170. for dependent := range g.dependents[node] {
  171. last := g.dependencies.removeFromDepmap(dependent, node)
  172. text.Debugln("pruning dependent", dependent, last)
  173. if last {
  174. pruned = append(pruned, g.Prune(dependent)...)
  175. }
  176. }
  177. delete(g.dependents, node)
  178. // Remove all edges from node to the things it depends on.
  179. for dependency := range g.dependencies[node] {
  180. last := g.dependents.removeFromDepmap(dependency, node)
  181. text.Debugln("pruning dependency", dependency, last)
  182. if last {
  183. pruned = append(pruned, g.Prune(dependency)...)
  184. }
  185. }
  186. delete(g.dependencies, node)
  187. // Finally, remove the node itself.
  188. delete(g.nodes, node)
  189. return pruned
  190. }
  191. func (g *Graph[T, V]) remove(node T) {
  192. // Remove edges from things that depend on `node`.
  193. for dependent := range g.dependents[node] {
  194. g.dependencies.removeFromDepmap(dependent, node)
  195. }
  196. delete(g.dependents, node)
  197. // Remove all edges from node to the things it depends on.
  198. for dependency := range g.dependencies[node] {
  199. g.dependents.removeFromDepmap(dependency, node)
  200. }
  201. delete(g.dependencies, node)
  202. // Finally, remove the node itself.
  203. delete(g.nodes, node)
  204. }
  205. func (g *Graph[T, V]) Dependencies(child T) NodeSet[T] {
  206. return g.buildTransitive(child, g.ImmediateDependencies)
  207. }
  208. func (g *Graph[T, V]) ImmediateDependencies(node T) NodeSet[T] {
  209. return g.dependencies[node]
  210. }
  211. func (g *Graph[T, V]) Dependents(parent T) NodeSet[T] {
  212. return g.buildTransitive(parent, g.immediateDependents)
  213. }
  214. func (g *Graph[T, V]) immediateDependents(node T) NodeSet[T] {
  215. return g.dependents[node]
  216. }
  217. func (g *Graph[T, V]) clone() *Graph[T, V] {
  218. return &Graph[T, V]{
  219. dependencies: g.dependencies.copy(),
  220. dependents: g.dependents.copy(),
  221. nodes: g.nodes.copy(),
  222. nodeInfo: g.nodeInfo, // not copied, as it is not modified
  223. }
  224. }
  225. // buildTransitive starts at `root` and continues calling `nextFn` to keep discovering more nodes until
  226. // the graph cannot produce any more. It returns the set of all discovered nodes.
  227. func (g *Graph[T, V]) buildTransitive(root T, nextFn func(T) NodeSet[T]) NodeSet[T] {
  228. if _, ok := g.nodes[root]; !ok {
  229. return nil
  230. }
  231. out := make(NodeSet[T])
  232. searchNext := []T{root}
  233. for len(searchNext) > 0 {
  234. // List of new nodes from this layer of the dependency graph. This is
  235. // assigned to `searchNext` at the end of the outer "discovery" loop.
  236. discovered := []T{}
  237. for _, node := range searchNext {
  238. // For each node to discover, find the next nodes.
  239. for nextNode := range nextFn(node) {
  240. // If we have not seen the node before, add it to the output as well
  241. // as the list of nodes to traverse in the next iteration.
  242. if _, ok := out[nextNode]; !ok {
  243. out[nextNode] = true
  244. discovered = append(discovered, nextNode)
  245. }
  246. }
  247. }
  248. searchNext = discovered
  249. }
  250. return out
  251. }
  252. func (s NodeSet[T]) copy() NodeSet[T] {
  253. out := make(NodeSet[T], len(s))
  254. for k, v := range s {
  255. out[k] = v
  256. }
  257. return out
  258. }
  259. func (dm DepMap[T]) copy() DepMap[T] {
  260. out := make(DepMap[T], len(dm))
  261. for k := range dm {
  262. out[k] = dm[k].copy()
  263. }
  264. return out
  265. }
  266. func (dm DepMap[T]) addNodeToNodeset(key, node T) {
  267. nodes, ok := dm[key]
  268. if !ok {
  269. nodes = make(NodeSet[T])
  270. dm[key] = nodes
  271. }
  272. nodes[node] = true
  273. }