Added -j2
[ruby.git] / lib / tsort.rb
blobdbaed454159f34bf7973ee33caf33116d42869f1
1 # frozen_string_literal: true
3 #--
4 # tsort.rb - provides a module for topological sorting and strongly connected components.
5 #++
9 # TSort implements topological sorting using Tarjan's algorithm for
10 # strongly connected components.
12 # TSort is designed to be able to be used with any object which can be
13 # interpreted as a directed graph.
15 # TSort requires two methods to interpret an object as a graph,
16 # tsort_each_node and tsort_each_child.
18 # * tsort_each_node is used to iterate for all nodes over a graph.
19 # * tsort_each_child is used to iterate for child nodes of a given node.
21 # The equality of nodes are defined by eql? and hash since
22 # TSort uses Hash internally.
24 # == A Simple Example
26 # The following example demonstrates how to mix the TSort module into an
27 # existing class (in this case, Hash). Here, we're treating each key in
28 # the hash as a node in the graph, and so we simply alias the required
29 # #tsort_each_node method to Hash's #each_key method. For each key in the
30 # hash, the associated value is an array of the node's child nodes. This
31 # choice in turn leads to our implementation of the required #tsort_each_child
32 # method, which fetches the array of child nodes and then iterates over that
33 # array using the user-supplied block.
35 #   require 'tsort'
37 #   class Hash
38 #     include TSort
39 #     alias tsort_each_node each_key
40 #     def tsort_each_child(node, &block)
41 #       fetch(node).each(&block)
42 #     end
43 #   end
45 #   {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
46 #   #=> [3, 2, 1, 4]
48 #   {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
49 #   #=> [[4], [2, 3], [1]]
51 # == A More Realistic Example
53 # A very simple `make' like tool can be implemented as follows:
55 #   require 'tsort'
57 #   class Make
58 #     def initialize
59 #       @dep = {}
60 #       @dep.default = []
61 #     end
63 #     def rule(outputs, inputs=[], &block)
64 #       triple = [outputs, inputs, block]
65 #       outputs.each {|f| @dep[f] = [triple]}
66 #       @dep[triple] = inputs
67 #     end
69 #     def build(target)
70 #       each_strongly_connected_component_from(target) {|ns|
71 #         if ns.length != 1
72 #           fs = ns.delete_if {|n| Array === n}
73 #           raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
74 #         end
75 #         n = ns.first
76 #         if Array === n
77 #           outputs, inputs, block = n
78 #           inputs_time = inputs.map {|f| File.mtime f}.max
79 #           begin
80 #             outputs_time = outputs.map {|f| File.mtime f}.min
81 #           rescue Errno::ENOENT
82 #             outputs_time = nil
83 #           end
84 #           if outputs_time == nil ||
85 #              inputs_time != nil && outputs_time <= inputs_time
86 #             sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
87 #             block.call
88 #           end
89 #         end
90 #       }
91 #     end
93 #     def tsort_each_child(node, &block)
94 #       @dep[node].each(&block)
95 #     end
96 #     include TSort
97 #   end
99 #   def command(arg)
100 #     print arg, "\n"
101 #     system arg
102 #   end
104 #   m = Make.new
105 #   m.rule(%w[t1]) { command 'date > t1' }
106 #   m.rule(%w[t2]) { command 'date > t2' }
107 #   m.rule(%w[t3]) { command 'date > t3' }
108 #   m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
109 #   m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
110 #   m.build('t5')
112 # == Bugs
114 # * 'tsort.rb' is wrong name because this library uses
115 #   Tarjan's algorithm for strongly connected components.
116 #   Although 'strongly_connected_components.rb' is correct but too long.
118 # == References
120 # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
121 # <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
124 module TSort
126   VERSION = "0.2.0"
128   class Cyclic < StandardError
129   end
131   # Returns a topologically sorted array of nodes.
132   # The array is sorted from children to parents, i.e.
133   # the first element has no child and the last node has no parent.
134   #
135   # If there is a cycle, TSort::Cyclic is raised.
136   #
137   #   class G
138   #     include TSort
139   #     def initialize(g)
140   #       @g = g
141   #     end
142   #     def tsort_each_child(n, &b) @g[n].each(&b) end
143   #     def tsort_each_node(&b) @g.each_key(&b) end
144   #   end
145   #
146   #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
147   #   p graph.tsort #=> [4, 2, 3, 1]
148   #
149   #   graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
150   #   p graph.tsort # raises TSort::Cyclic
151   #
152   def tsort
153     each_node = method(:tsort_each_node)
154     each_child = method(:tsort_each_child)
155     TSort.tsort(each_node, each_child)
156   end
158   # Returns a topologically sorted array of nodes.
159   # The array is sorted from children to parents, i.e.
160   # the first element has no child and the last node has no parent.
161   #
162   # The graph is represented by _each_node_ and _each_child_.
163   # _each_node_ should have +call+ method which yields for each node in the graph.
164   # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
165   #
166   # If there is a cycle, TSort::Cyclic is raised.
167   #
168   #   g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
169   #   each_node = lambda {|&b| g.each_key(&b) }
170   #   each_child = lambda {|n, &b| g[n].each(&b) }
171   #   p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
172   #
173   #   g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
174   #   each_node = lambda {|&b| g.each_key(&b) }
175   #   each_child = lambda {|n, &b| g[n].each(&b) }
176   #   p TSort.tsort(each_node, each_child) # raises TSort::Cyclic
177   #
178   def self.tsort(each_node, each_child)
179     tsort_each(each_node, each_child).to_a
180   end
182   # The iterator version of the #tsort method.
183   # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
184   # modification of _obj_ during the iteration may lead to unexpected results.
185   #
186   # #tsort_each returns +nil+.
187   # If there is a cycle, TSort::Cyclic is raised.
188   #
189   #   class G
190   #     include TSort
191   #     def initialize(g)
192   #       @g = g
193   #     end
194   #     def tsort_each_child(n, &b) @g[n].each(&b) end
195   #     def tsort_each_node(&b) @g.each_key(&b) end
196   #   end
197   #
198   #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
199   #   graph.tsort_each {|n| p n }
200   #   #=> 4
201   #   #   2
202   #   #   3
203   #   #   1
204   #
205   def tsort_each(&block) # :yields: node
206     each_node = method(:tsort_each_node)
207     each_child = method(:tsort_each_child)
208     TSort.tsort_each(each_node, each_child, &block)
209   end
211   # The iterator version of the TSort.tsort method.
212   #
213   # The graph is represented by _each_node_ and _each_child_.
214   # _each_node_ should have +call+ method which yields for each node in the graph.
215   # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
216   #
217   #   g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
218   #   each_node = lambda {|&b| g.each_key(&b) }
219   #   each_child = lambda {|n, &b| g[n].each(&b) }
220   #   TSort.tsort_each(each_node, each_child) {|n| p n }
221   #   #=> 4
222   #   #   2
223   #   #   3
224   #   #   1
225   #
226   def self.tsort_each(each_node, each_child) # :yields: node
227     return to_enum(__method__, each_node, each_child) unless block_given?
229     each_strongly_connected_component(each_node, each_child) {|component|
230       if component.size == 1
231         yield component.first
232       else
233         raise Cyclic.new("topological sort failed: #{component.inspect}")
234       end
235     }
236   end
238   # Returns strongly connected components as an array of arrays of nodes.
239   # The array is sorted from children to parents.
240   # Each elements of the array represents a strongly connected component.
241   #
242   #   class G
243   #     include TSort
244   #     def initialize(g)
245   #       @g = g
246   #     end
247   #     def tsort_each_child(n, &b) @g[n].each(&b) end
248   #     def tsort_each_node(&b) @g.each_key(&b) end
249   #   end
250   #
251   #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
252   #   p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
253   #
254   #   graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
255   #   p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
256   #
257   def strongly_connected_components
258     each_node = method(:tsort_each_node)
259     each_child = method(:tsort_each_child)
260     TSort.strongly_connected_components(each_node, each_child)
261   end
263   # Returns strongly connected components as an array of arrays of nodes.
264   # The array is sorted from children to parents.
265   # Each elements of the array represents a strongly connected component.
266   #
267   # The graph is represented by _each_node_ and _each_child_.
268   # _each_node_ should have +call+ method which yields for each node in the graph.
269   # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
270   #
271   #   g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
272   #   each_node = lambda {|&b| g.each_key(&b) }
273   #   each_child = lambda {|n, &b| g[n].each(&b) }
274   #   p TSort.strongly_connected_components(each_node, each_child)
275   #   #=> [[4], [2], [3], [1]]
276   #
277   #   g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
278   #   each_node = lambda {|&b| g.each_key(&b) }
279   #   each_child = lambda {|n, &b| g[n].each(&b) }
280   #   p TSort.strongly_connected_components(each_node, each_child)
281   #   #=> [[4], [2, 3], [1]]
282   #
283   def self.strongly_connected_components(each_node, each_child)
284     each_strongly_connected_component(each_node, each_child).to_a
285   end
287   # The iterator version of the #strongly_connected_components method.
288   # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
289   # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
290   # modification of _obj_ during the iteration may lead to unexpected results.
291   #
292   # #each_strongly_connected_component returns +nil+.
293   #
294   #   class G
295   #     include TSort
296   #     def initialize(g)
297   #       @g = g
298   #     end
299   #     def tsort_each_child(n, &b) @g[n].each(&b) end
300   #     def tsort_each_node(&b) @g.each_key(&b) end
301   #   end
302   #
303   #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
304   #   graph.each_strongly_connected_component {|scc| p scc }
305   #   #=> [4]
306   #   #   [2]
307   #   #   [3]
308   #   #   [1]
309   #
310   #   graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
311   #   graph.each_strongly_connected_component {|scc| p scc }
312   #   #=> [4]
313   #   #   [2, 3]
314   #   #   [1]
315   #
316   def each_strongly_connected_component(&block) # :yields: nodes
317     each_node = method(:tsort_each_node)
318     each_child = method(:tsort_each_child)
319     TSort.each_strongly_connected_component(each_node, each_child, &block)
320   end
322   # The iterator version of the TSort.strongly_connected_components method.
323   #
324   # The graph is represented by _each_node_ and _each_child_.
325   # _each_node_ should have +call+ method which yields for each node in the graph.
326   # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
327   #
328   #   g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
329   #   each_node = lambda {|&b| g.each_key(&b) }
330   #   each_child = lambda {|n, &b| g[n].each(&b) }
331   #   TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
332   #   #=> [4]
333   #   #   [2]
334   #   #   [3]
335   #   #   [1]
336   #
337   #   g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
338   #   each_node = lambda {|&b| g.each_key(&b) }
339   #   each_child = lambda {|n, &b| g[n].each(&b) }
340   #   TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
341   #   #=> [4]
342   #   #   [2, 3]
343   #   #   [1]
344   #
345   def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
346     return to_enum(__method__, each_node, each_child) unless block_given?
348     id_map = {}
349     stack = []
350     each_node.call {|node|
351       unless id_map.include? node
352         each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
353           yield c
354         }
355       end
356     }
357     nil
358   end
360   # Iterates over strongly connected component in the subgraph reachable from
361   # _node_.
362   #
363   # Return value is unspecified.
364   #
365   # #each_strongly_connected_component_from doesn't call #tsort_each_node.
366   #
367   #   class G
368   #     include TSort
369   #     def initialize(g)
370   #       @g = g
371   #     end
372   #     def tsort_each_child(n, &b) @g[n].each(&b) end
373   #     def tsort_each_node(&b) @g.each_key(&b) end
374   #   end
375   #
376   #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
377   #   graph.each_strongly_connected_component_from(2) {|scc| p scc }
378   #   #=> [4]
379   #   #   [2]
380   #
381   #   graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
382   #   graph.each_strongly_connected_component_from(2) {|scc| p scc }
383   #   #=> [4]
384   #   #   [2, 3]
385   #
386   def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
387     TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
388   end
390   # Iterates over strongly connected components in a graph.
391   # The graph is represented by _node_ and _each_child_.
392   #
393   # _node_ is the first node.
394   # _each_child_ should have +call+ method which takes a node argument
395   # and yields for each child node.
396   #
397   # Return value is unspecified.
398   #
399   # #TSort.each_strongly_connected_component_from is a class method and
400   # it doesn't need a class to represent a graph which includes TSort.
401   #
402   #   graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
403   #   each_child = lambda {|n, &b| graph[n].each(&b) }
404   #   TSort.each_strongly_connected_component_from(1, each_child) {|scc|
405   #     p scc
406   #   }
407   #   #=> [4]
408   #   #   [2, 3]
409   #   #   [1]
410   #
411   def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
412     return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
414     minimum_id = node_id = id_map[node] = id_map.size
415     stack_length = stack.length
416     stack << node
418     each_child.call(node) {|child|
419       if id_map.include? child
420         child_id = id_map[child]
421         minimum_id = child_id if child_id && child_id < minimum_id
422       else
423         sub_minimum_id =
424           each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
425             yield c
426           }
427         minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
428       end
429     }
431     if node_id == minimum_id
432       component = stack.slice!(stack_length .. -1)
433       component.each {|n| id_map[n] = nil}
434       yield component
435     end
437     minimum_id
438   end
440   # Should be implemented by a extended class.
441   #
442   # #tsort_each_node is used to iterate for all nodes over a graph.
443   #
444   def tsort_each_node # :yields: node
445     raise NotImplementedError.new
446   end
448   # Should be implemented by a extended class.
449   #
450   # #tsort_each_child is used to iterate for child nodes of _node_.
451   #
452   def tsort_each_child(node) # :yields: child
453     raise NotImplementedError.new
454   end