PageRenderTime 21ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/jruby-1.7.3/lib/ruby/1.8/tsort.rb

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Ruby | 290 lines | 102 code | 21 blank | 167 comment | 10 complexity | 9bed3c15986cce83dffc9821798040e4 MD5 | raw file
  1. #!/usr/bin/env ruby
  2. #--
  3. # tsort.rb - provides a module for topological sorting and strongly connected components.
  4. #++
  5. #
  6. #
  7. # TSort implements topological sorting using Tarjan's algorithm for
  8. # strongly connected components.
  9. #
  10. # TSort is designed to be able to be used with any object which can be
  11. # interpreted as a directed graph.
  12. #
  13. # TSort requires two methods to interpret an object as a graph,
  14. # tsort_each_node and tsort_each_child.
  15. #
  16. # * tsort_each_node is used to iterate for all nodes over a graph.
  17. # * tsort_each_child is used to iterate for child nodes of a given node.
  18. #
  19. # The equality of nodes are defined by eql? and hash since
  20. # TSort uses Hash internally.
  21. #
  22. # == A Simple Example
  23. #
  24. # The following example demonstrates how to mix the TSort module into an
  25. # existing class (in this case, Hash). Here, we're treating each key in
  26. # the hash as a node in the graph, and so we simply alias the required
  27. # #tsort_each_node method to Hash's #each_key method. For each key in the
  28. # hash, the associated value is an array of the node's child nodes. This
  29. # choice in turn leads to our implementation of the required #tsort_each_child
  30. # method, which fetches the array of child nodes and then iterates over that
  31. # array using the user-supplied block.
  32. #
  33. # require 'tsort'
  34. #
  35. # class Hash
  36. # include TSort
  37. # alias tsort_each_node each_key
  38. # def tsort_each_child(node, &block)
  39. # fetch(node).each(&block)
  40. # end
  41. # end
  42. #
  43. # {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
  44. # #=> [3, 2, 1, 4]
  45. #
  46. # {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
  47. # #=> [[4], [2, 3], [1]]
  48. #
  49. # == A More Realistic Example
  50. #
  51. # A very simple `make' like tool can be implemented as follows:
  52. #
  53. # require 'tsort'
  54. #
  55. # class Make
  56. # def initialize
  57. # @dep = {}
  58. # @dep.default = []
  59. # end
  60. #
  61. # def rule(outputs, inputs=[], &block)
  62. # triple = [outputs, inputs, block]
  63. # outputs.each {|f| @dep[f] = [triple]}
  64. # @dep[triple] = inputs
  65. # end
  66. #
  67. # def build(target)
  68. # each_strongly_connected_component_from(target) {|ns|
  69. # if ns.length != 1
  70. # fs = ns.delete_if {|n| Array === n}
  71. # raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
  72. # end
  73. # n = ns.first
  74. # if Array === n
  75. # outputs, inputs, block = n
  76. # inputs_time = inputs.map {|f| File.mtime f}.max
  77. # begin
  78. # outputs_time = outputs.map {|f| File.mtime f}.min
  79. # rescue Errno::ENOENT
  80. # outputs_time = nil
  81. # end
  82. # if outputs_time == nil ||
  83. # inputs_time != nil && outputs_time <= inputs_time
  84. # sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
  85. # block.call
  86. # end
  87. # end
  88. # }
  89. # end
  90. #
  91. # def tsort_each_child(node, &block)
  92. # @dep[node].each(&block)
  93. # end
  94. # include TSort
  95. # end
  96. #
  97. # def command(arg)
  98. # print arg, "\n"
  99. # system arg
  100. # end
  101. #
  102. # m = Make.new
  103. # m.rule(%w[t1]) { command 'date > t1' }
  104. # m.rule(%w[t2]) { command 'date > t2' }
  105. # m.rule(%w[t3]) { command 'date > t3' }
  106. # m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
  107. # m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
  108. # m.build('t5')
  109. #
  110. # == Bugs
  111. #
  112. # * 'tsort.rb' is wrong name because this library uses
  113. # Tarjan's algorithm for strongly connected components.
  114. # Although 'strongly_connected_components.rb' is correct but too long.
  115. #
  116. # == References
  117. #
  118. # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
  119. # <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
  120. #
  121. module TSort
  122. class Cyclic < StandardError
  123. end
  124. #
  125. # Returns a topologically sorted array of nodes.
  126. # The array is sorted from children to parents, i.e.
  127. # the first element has no child and the last node has no parent.
  128. #
  129. # If there is a cycle, TSort::Cyclic is raised.
  130. #
  131. def tsort
  132. result = []
  133. tsort_each {|element| result << element}
  134. result
  135. end
  136. #
  137. # The iterator version of the #tsort method.
  138. # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
  139. # modification of _obj_ during the iteration may lead to unexpected results.
  140. #
  141. # #tsort_each returns +nil+.
  142. # If there is a cycle, TSort::Cyclic is raised.
  143. #
  144. def tsort_each # :yields: node
  145. each_strongly_connected_component {|component|
  146. if component.size == 1
  147. yield component.first
  148. else
  149. raise Cyclic.new("topological sort failed: #{component.inspect}")
  150. end
  151. }
  152. end
  153. #
  154. # Returns strongly connected components as an array of arrays of nodes.
  155. # The array is sorted from children to parents.
  156. # Each elements of the array represents a strongly connected component.
  157. #
  158. def strongly_connected_components
  159. result = []
  160. each_strongly_connected_component {|component| result << component}
  161. result
  162. end
  163. #
  164. # The iterator version of the #strongly_connected_components method.
  165. # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
  166. # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
  167. # modification of _obj_ during the iteration may lead to unexpected results.
  168. #
  169. #
  170. # #each_strongly_connected_component returns +nil+.
  171. #
  172. def each_strongly_connected_component # :yields: nodes
  173. id_map = {}
  174. stack = []
  175. tsort_each_node {|node|
  176. unless id_map.include? node
  177. each_strongly_connected_component_from(node, id_map, stack) {|c|
  178. yield c
  179. }
  180. end
  181. }
  182. nil
  183. end
  184. #
  185. # Iterates over strongly connected component in the subgraph reachable from
  186. # _node_.
  187. #
  188. # Return value is unspecified.
  189. #
  190. # #each_strongly_connected_component_from doesn't call #tsort_each_node.
  191. #
  192. def each_strongly_connected_component_from(node, id_map={}, stack=[]) # :yields: nodes
  193. minimum_id = node_id = id_map[node] = id_map.size
  194. stack_length = stack.length
  195. stack << node
  196. tsort_each_child(node) {|child|
  197. if id_map.include? child
  198. child_id = id_map[child]
  199. minimum_id = child_id if child_id && child_id < minimum_id
  200. else
  201. sub_minimum_id =
  202. each_strongly_connected_component_from(child, id_map, stack) {|c|
  203. yield c
  204. }
  205. minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
  206. end
  207. }
  208. if node_id == minimum_id
  209. component = stack.slice!(stack_length .. -1)
  210. component.each {|n| id_map[n] = nil}
  211. yield component
  212. end
  213. minimum_id
  214. end
  215. #
  216. # Should be implemented by a extended class.
  217. #
  218. # #tsort_each_node is used to iterate for all nodes over a graph.
  219. #
  220. def tsort_each_node # :yields: node
  221. raise NotImplementedError.new
  222. end
  223. #
  224. # Should be implemented by a extended class.
  225. #
  226. # #tsort_each_child is used to iterate for child nodes of _node_.
  227. #
  228. def tsort_each_child(node) # :yields: child
  229. raise NotImplementedError.new
  230. end
  231. end
  232. if __FILE__ == $0
  233. require 'test/unit'
  234. class TSortHash < Hash # :nodoc:
  235. include TSort
  236. alias tsort_each_node each_key
  237. def tsort_each_child(node, &block)
  238. fetch(node).each(&block)
  239. end
  240. end
  241. class TSortArray < Array # :nodoc:
  242. include TSort
  243. alias tsort_each_node each_index
  244. def tsort_each_child(node, &block)
  245. fetch(node).each(&block)
  246. end
  247. end
  248. class TSortTest < Test::Unit::TestCase # :nodoc:
  249. def test_dag
  250. h = TSortHash[{1=>[2, 3], 2=>[3], 3=>[]}]
  251. assert_equal([3, 2, 1], h.tsort)
  252. assert_equal([[3], [2], [1]], h.strongly_connected_components)
  253. end
  254. def test_cycle
  255. h = TSortHash[{1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}]
  256. assert_equal([[4], [2, 3], [1]],
  257. h.strongly_connected_components.map {|nodes| nodes.sort})
  258. assert_raise(TSort::Cyclic) { h.tsort }
  259. end
  260. def test_array
  261. a = TSortArray[[1], [0], [0], [2]]
  262. assert_equal([[0, 1], [2], [3]],
  263. a.strongly_connected_components.map {|nodes| nodes.sort})
  264. a = TSortArray[[], [0]]
  265. assert_equal([[0], [1]],
  266. a.strongly_connected_components.map {|nodes| nodes.sort})
  267. end
  268. end
  269. end