# TSort implements topological sorting using Tarjan's algorithm for strongly # connected components. # # TSort is designed to be able to be used with any object which can be # interpreted as a directed graph. # # TSort requires two methods to interpret an object as a graph, tsort_each_node # and tsort_each_child. # # * tsort_each_node is used to iterate for all nodes over a graph. # * tsort_each_child is used to iterate for child nodes of a given node. # # # The equality of nodes are defined by eql? and hash since TSort uses Hash # internally. # # ## A Simple Example # # The following example demonstrates how to mix the TSort module into an # existing class (in this case, Hash). Here, we're treating each key in the hash # as a node in the graph, and so we simply alias the required #tsort_each_node # method to Hash's #each_key method. For each key in the hash, the associated # value is an array of the node's child nodes. This choice in turn leads to our # implementation of the required #tsort_each_child method, which fetches the # array of child nodes and then iterates over that array using the user-supplied # block. # # require 'tsort' # # class Hash # include TSort # alias tsort_each_node each_key # def tsort_each_child(node, &block) # fetch(node).each(&block) # end # end # # {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort # #=> [3, 2, 1, 4] # # {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components # #=> [[4], [2, 3], [1]] # # ## A More Realistic Example # # A very simple `make' like tool can be implemented as follows: # # require 'tsort' # # class Make # def initialize # @dep = {} # @dep.default = [] # end # # def rule(outputs, inputs=[], &block) # triple = [outputs, inputs, block] # outputs.each {|f| @dep[f] = [triple]} # @dep[triple] = inputs # end # # def build(target) # each_strongly_connected_component_from(target) {|ns| # if ns.length != 1 # fs = ns.delete_if {|n| Array === n} # raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") # end # n = ns.first # if Array === n # outputs, inputs, block = n # inputs_time = inputs.map {|f| File.mtime f}.max # begin # outputs_time = outputs.map {|f| File.mtime f}.min # rescue Errno::ENOENT # outputs_time = nil # end # if outputs_time == nil || # inputs_time != nil && outputs_time <= inputs_time # sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i # block.call # end # end # } # end # # def tsort_each_child(node, &block) # @dep[node].each(&block) # end # include TSort # end # # def command(arg) # print arg, "\n" # system arg # end # # m = Make.new # m.rule(%w[t1]) { command 'date > t1' } # m.rule(%w[t2]) { command 'date > t2' } # m.rule(%w[t3]) { command 'date > t3' } # m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' } # m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' } # m.build('t5') # # ## Bugs # # * 'tsort.rb' is wrong name because this library uses Tarjan's algorithm for # strongly connected components. Although 'strongly_connected_components.rb' # is correct but too long. # # # ## References # # 1. Tarjan, "Depth First Search and Linear Graph Algorithms", # # # *SIAM Journal on Computing*, Vol. 1, No. 2, pp. 146-160, June 1972. module TSort[Node] : TSort::_Sortable[Node] # The iterator version of the TSort.strongly_connected_components method. # # The graph is represented by *each_node* and *each_child*. *each_node* should # have `call` method which yields for each node in the graph. *each_child* # should have `call` method which takes a node argument and yields for each # child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # #=> [4] # # [2] # # [3] # # [1] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # #=> [4] # # [2, 3] # # [1] # def self.each_strongly_connected_component: [T] (_EachNode[T] each_node, _EachChild[T] each_child) { (Array[T]) -> void } -> void | [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Enumerator[Array[T], void] | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) { (Array[T]) -> void } -> void | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Enumerator[Array[T], void] # Iterates over strongly connected components in a graph. The graph is # represented by *node* and *each_child*. # # *node* is the first node. *each_child* should have `call` method which takes a # node argument and yields for each child node. # # Return value is unspecified. # # #TSort.each_strongly_connected_component_from is a class method and it doesn't # need a class to represent a graph which includes TSort. # # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_child = lambda {|n, &b| graph[n].each(&b) } # TSort.each_strongly_connected_component_from(1, each_child) {|scc| # p scc # } # #=> [4] # # [2, 3] # # [1] # def self.each_strongly_connected_component_from: [T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) { (Array[T]) -> void } -> void | [T] (T node, _EachChild[T] each_child, ?untyped id_map, ?untyped stack) -> Enumerator[Array[T], void] | [T] (T node, ^(T) { (T) -> void } -> void each_child, ?untyped id_map, ?untyped stack) { (Array[T]) -> void } -> void | [T] (T node, ^(T) { (T) -> void } -> void each_child, ?untyped id_map, ?untyped stack) -> Enumerator[Array[T], void] # Returns strongly connected components as an array of arrays of nodes. The # array is sorted from children to parents. Each elements of the array # represents a strongly connected component. # # The graph is represented by *each_node* and *each_child*. *each_node* should # have `call` method which yields for each node in the graph. *each_child* # should have `call` method which takes a node argument and yields for each # child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p TSort.strongly_connected_components(each_node, each_child) # #=> [[4], [2], [3], [1]] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p TSort.strongly_connected_components(each_node, each_child) # #=> [[4], [2, 3], [1]] # def self.strongly_connected_components: [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Array[Array[T]] | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Array[Array[T]] # Returns a topologically sorted array of nodes. The array is sorted from # children to parents, i.e. the first element has no child and the last node has # no parent. # # The graph is represented by *each_node* and *each_child*. *each_node* should # have `call` method which yields for each node in the graph. *each_child* # should have `call` method which takes a node argument and yields for each # child node. # # If there is a cycle, TSort::Cyclic is raised. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p TSort.tsort(each_node, each_child) # raises TSort::Cyclic # def self.tsort: [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Array[T] | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Array[T] # The iterator version of the TSort.tsort method. # # The graph is represented by *each_node* and *each_child*. *each_node* should # have `call` method which yields for each node in the graph. *each_child* # should have `call` method which takes a node argument and yields for each # child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # TSort.tsort_each(each_node, each_child) {|n| p n } # #=> 4 # # 2 # # 3 # # 1 # def self.tsort_each: [T] (_EachNode[T] each_node, _EachChild[T] each_child) { (T) -> void } -> void | [T] (_EachNode[T] each_node, _EachChild[T] each_child) -> Enumerator[T, void] | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) { (T) -> void } -> void | [T] (^() { (T) -> void } -> void each_node, ^(T) { (T) -> void } -> void each_child) -> Enumerator[T, void] # The iterator version of the #strongly_connected_components method. # *`obj*.each_strongly_connected_component` is similar to # *`obj*.strongly_connected_components.each`, but modification of *obj* during # the iteration may lead to unexpected results. # # #each_strongly_connected_component returns `nil`. # # class G # include TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.each_strongly_connected_component {|scc| p scc } # #=> [4] # # [2] # # [3] # # [1] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # graph.each_strongly_connected_component {|scc| p scc } # #=> [4] # # [2, 3] # # [1] # def each_strongly_connected_component: () { (Array[Node]) -> void } -> void | () -> Enumerator[Array[Node], void] # Iterates over strongly connected component in the subgraph reachable from # *node*. # # Return value is unspecified. # # #each_strongly_connected_component_from doesn't call #tsort_each_node. # # class G # include TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.each_strongly_connected_component_from(2) {|scc| p scc } # #=> [4] # # [2] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # graph.each_strongly_connected_component_from(2) {|scc| p scc } # #=> [4] # # [2, 3] # def each_strongly_connected_component_from: (Node, ?untyped id_map, ?untyped stack) { (Array[Node]) -> void } -> void | (Node, ?untyped id_map, ?untyped stack) -> Enumerator[Array[Node], void] # Returns strongly connected components as an array of arrays of nodes. The # array is sorted from children to parents. Each elements of the array # represents a strongly connected component. # # class G # include TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # p graph.strongly_connected_components #=> [[4], [2], [3], [1]] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # p graph.strongly_connected_components #=> [[4], [2, 3], [1]] # def strongly_connected_components: () -> Array[Array[Node]] # Returns a topologically sorted array of nodes. The array is sorted from # children to parents, i.e. the first element has no child and the last node has # no parent. # # If there is a cycle, TSort::Cyclic is raised. # # class G # include TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # p graph.tsort #=> [4, 2, 3, 1] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # p graph.tsort # raises TSort::Cyclic # def tsort: () -> Array[Node] # The iterator version of the #tsort method. *`obj*.tsort_each` is similar to # *`obj*.tsort.each`, but modification of *obj* during the iteration may lead to # unexpected results. # # #tsort_each returns `nil`. If there is a cycle, TSort::Cyclic is raised. # # class G # include TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.tsort_each {|n| p n } # #=> 4 # # 2 # # 3 # # 1 # def tsort_each: () { (Node) -> void } -> void | () -> Enumerator[Node, void] end