rubysl-tsort 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dbef2bc585a86be57ed019dab0b8bb3fefbcc9ef
4
+ data.tar.gz: 53b88f63caead7e17da6a47ca37aedc51ee30adc
5
+ SHA512:
6
+ metadata.gz: 86f2e9798814f5dba80e550ea1046625fc7c226547607d7526b16155f81a5b4263fe27b207f147b54e127d8d43db25efd0f89bbc1c61c0fd1066c2c510c33140
7
+ data.tar.gz: a6d219f33ada7967cbf66cc63aecd9ce7980345a3ef106ed1d42ec158310a1e0c1c297adbbfb2fee41f54aa405ac6e475438bcd5c8c479a875e861cd7b391af5
data/.gitignore CHANGED
@@ -15,4 +15,3 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- .rbx
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.8.7
7
+ - rbx-nightly-18mode
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RubySL::Tsort
1
+ # Rubysl::Tsort
2
2
 
3
3
  TODO: Write a gem description
4
4
 
@@ -24,6 +24,6 @@ TODO: Write usage instructions here
24
24
 
25
25
  1. Fork it
26
26
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Added some feature'`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
28
  4. Push to the branch (`git push origin my-new-feature`)
29
29
  5. Create new Pull Request
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ require "rubysl/tsort/version"
2
+ require "rubysl/tsort/tsort"
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # tsort.rb - provides a module for topological sorting and strongly connected components.
4
+ #++
5
+ #
6
+
7
+ #
8
+ # TSort implements topological sorting using Tarjan's algorithm for
9
+ # strongly connected components.
10
+ #
11
+ # TSort is designed to be able to be used with any object which can be
12
+ # interpreted as a directed graph.
13
+ #
14
+ # TSort requires two methods to interpret an object as a graph,
15
+ # tsort_each_node and tsort_each_child.
16
+ #
17
+ # * tsort_each_node is used to iterate for all nodes over a graph.
18
+ # * tsort_each_child is used to iterate for child nodes of a given node.
19
+ #
20
+ # The equality of nodes are defined by eql? and hash since
21
+ # TSort uses Hash internally.
22
+ #
23
+ # == A Simple Example
24
+ #
25
+ # The following example demonstrates how to mix the TSort module into an
26
+ # existing class (in this case, Hash). Here, we're treating each key in
27
+ # the hash as a node in the graph, and so we simply alias the required
28
+ # #tsort_each_node method to Hash's #each_key method. For each key in the
29
+ # hash, the associated value is an array of the node's child nodes. This
30
+ # choice in turn leads to our implementation of the required #tsort_each_child
31
+ # method, which fetches the array of child nodes and then iterates over that
32
+ # array using the user-supplied block.
33
+ #
34
+ # require 'tsort'
35
+ #
36
+ # class Hash
37
+ # include TSort
38
+ # alias tsort_each_node each_key
39
+ # def tsort_each_child(node, &block)
40
+ # fetch(node).each(&block)
41
+ # end
42
+ # end
43
+ #
44
+ # {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
45
+ # #=> [3, 2, 1, 4]
46
+ #
47
+ # {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
48
+ # #=> [[4], [2, 3], [1]]
49
+ #
50
+ # == A More Realistic Example
51
+ #
52
+ # A very simple `make' like tool can be implemented as follows:
53
+ #
54
+ # require 'tsort'
55
+ #
56
+ # class Make
57
+ # def initialize
58
+ # @dep = {}
59
+ # @dep.default = []
60
+ # end
61
+ #
62
+ # def rule(outputs, inputs=[], &block)
63
+ # triple = [outputs, inputs, block]
64
+ # outputs.each {|f| @dep[f] = [triple]}
65
+ # @dep[triple] = inputs
66
+ # end
67
+ #
68
+ # def build(target)
69
+ # each_strongly_connected_component_from(target) {|ns|
70
+ # if ns.length != 1
71
+ # fs = ns.delete_if {|n| Array === n}
72
+ # raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
73
+ # end
74
+ # n = ns.first
75
+ # if Array === n
76
+ # outputs, inputs, block = n
77
+ # inputs_time = inputs.map {|f| File.mtime f}.max
78
+ # begin
79
+ # outputs_time = outputs.map {|f| File.mtime f}.min
80
+ # rescue Errno::ENOENT
81
+ # outputs_time = nil
82
+ # end
83
+ # if outputs_time == nil ||
84
+ # inputs_time != nil && outputs_time <= inputs_time
85
+ # sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
86
+ # block.call
87
+ # end
88
+ # end
89
+ # }
90
+ # end
91
+ #
92
+ # def tsort_each_child(node, &block)
93
+ # @dep[node].each(&block)
94
+ # end
95
+ # include TSort
96
+ # end
97
+ #
98
+ # def command(arg)
99
+ # print arg, "\n"
100
+ # system arg
101
+ # end
102
+ #
103
+ # m = Make.new
104
+ # m.rule(%w[t1]) { command 'date > t1' }
105
+ # m.rule(%w[t2]) { command 'date > t2' }
106
+ # m.rule(%w[t3]) { command 'date > t3' }
107
+ # m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
108
+ # m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
109
+ # m.build('t5')
110
+ #
111
+ # == Bugs
112
+ #
113
+ # * 'tsort.rb' is wrong name because this library uses
114
+ # Tarjan's algorithm for strongly connected components.
115
+ # Although 'strongly_connected_components.rb' is correct but too long.
116
+ #
117
+ # == References
118
+ #
119
+ # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
120
+ # <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
121
+ #
122
+
123
+ module TSort
124
+ class Cyclic < StandardError
125
+ end
126
+
127
+ #
128
+ # Returns a topologically sorted array of nodes.
129
+ # The array is sorted from children to parents, i.e.
130
+ # the first element has no child and the last node has no parent.
131
+ #
132
+ # If there is a cycle, TSort::Cyclic is raised.
133
+ #
134
+ def tsort
135
+ result = []
136
+ tsort_each {|element| result << element}
137
+ result
138
+ end
139
+
140
+ #
141
+ # The iterator version of the #tsort method.
142
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
143
+ # modification of _obj_ during the iteration may lead to unexpected results.
144
+ #
145
+ # #tsort_each returns +nil+.
146
+ # If there is a cycle, TSort::Cyclic is raised.
147
+ #
148
+ def tsort_each # :yields: node
149
+ each_strongly_connected_component {|component|
150
+ if component.size == 1
151
+ yield component.first
152
+ else
153
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
154
+ end
155
+ }
156
+ end
157
+
158
+ #
159
+ # Returns strongly connected components as an array of arrays of nodes.
160
+ # The array is sorted from children to parents.
161
+ # Each elements of the array represents a strongly connected component.
162
+ #
163
+ def strongly_connected_components
164
+ result = []
165
+ each_strongly_connected_component {|component| result << component}
166
+ result
167
+ end
168
+
169
+ #
170
+ # The iterator version of the #strongly_connected_components method.
171
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
172
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
173
+ # modification of _obj_ during the iteration may lead to unexpected results.
174
+ #
175
+ #
176
+ # #each_strongly_connected_component returns +nil+.
177
+ #
178
+ def each_strongly_connected_component # :yields: nodes
179
+ id_map = {}
180
+ stack = []
181
+ tsort_each_node {|node|
182
+ unless id_map.include? node
183
+ each_strongly_connected_component_from(node, id_map, stack) {|c|
184
+ yield c
185
+ }
186
+ end
187
+ }
188
+ nil
189
+ end
190
+
191
+ #
192
+ # Iterates over strongly connected component in the subgraph reachable from
193
+ # _node_.
194
+ #
195
+ # Return value is unspecified.
196
+ #
197
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
198
+ #
199
+ def each_strongly_connected_component_from(node, id_map={}, stack=[]) # :yields: nodes
200
+ minimum_id = node_id = id_map[node] = id_map.size
201
+ stack_length = stack.length
202
+ stack << node
203
+
204
+ tsort_each_child(node) {|child|
205
+ if id_map.include? child
206
+ child_id = id_map[child]
207
+ minimum_id = child_id if child_id && child_id < minimum_id
208
+ else
209
+ sub_minimum_id =
210
+ each_strongly_connected_component_from(child, id_map, stack) {|c|
211
+ yield c
212
+ }
213
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
214
+ end
215
+ }
216
+
217
+ if node_id == minimum_id
218
+ component = stack.slice!(stack_length .. -1)
219
+ component.each {|n| id_map[n] = nil}
220
+ yield component
221
+ end
222
+
223
+ minimum_id
224
+ end
225
+
226
+ #
227
+ # Should be implemented by a extended class.
228
+ #
229
+ # #tsort_each_node is used to iterate for all nodes over a graph.
230
+ #
231
+ def tsort_each_node # :yields: node
232
+ raise NotImplementedError.new
233
+ end
234
+
235
+ #
236
+ # Should be implemented by a extended class.
237
+ #
238
+ # #tsort_each_child is used to iterate for child nodes of _node_.
239
+ #
240
+ def tsort_each_child(node) # :yields: child
241
+ raise NotImplementedError.new
242
+ end
243
+ end
244
+
245
+ if __FILE__ == $0
246
+ require 'test/unit'
247
+
248
+ class TSortHash < Hash # :nodoc:
249
+ include TSort
250
+ alias tsort_each_node each_key
251
+ def tsort_each_child(node, &block)
252
+ fetch(node).each(&block)
253
+ end
254
+ end
255
+
256
+ class TSortArray < Array # :nodoc:
257
+ include TSort
258
+ alias tsort_each_node each_index
259
+ def tsort_each_child(node, &block)
260
+ fetch(node).each(&block)
261
+ end
262
+ end
263
+
264
+ class TSortTest < Test::Unit::TestCase # :nodoc:
265
+ def test_dag
266
+ h = TSortHash[{1=>[2, 3], 2=>[3], 3=>[]}]
267
+ assert_equal([3, 2, 1], h.tsort)
268
+ assert_equal([[3], [2], [1]], h.strongly_connected_components)
269
+ end
270
+
271
+ def test_cycle
272
+ h = TSortHash[{1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}]
273
+ assert_equal([[4], [2, 3], [1]],
274
+ h.strongly_connected_components.map {|nodes| nodes.sort})
275
+ assert_raise(TSort::Cyclic) { h.tsort }
276
+ end
277
+
278
+ def test_array
279
+ a = TSortArray[[1], [0], [0], [2]]
280
+ assert_equal([[0, 1], [2], [3]],
281
+ a.strongly_connected_components.map {|nodes| nodes.sort})
282
+
283
+ a = TSortArray[[], [0]]
284
+ assert_equal([[0], [1]],
285
+ a.strongly_connected_components.map {|nodes| nodes.sort})
286
+ end
287
+ end
288
+
289
+ end
290
+
@@ -0,0 +1,5 @@
1
+ module RubySL
2
+ module TSort
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
data/lib/tsort.rb ADDED
@@ -0,0 +1 @@
1
+ require "rubysl/tsort"
data/rubysl-tsort.gemspec CHANGED
@@ -1,22 +1,21 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/rubysl-tsort/version', __FILE__)
1
+ # coding: utf-8
2
+ require './lib/rubysl/tsort/version'
3
3
 
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Brian Shirai"]
6
- gem.email = ["brixen@gmail.com"]
7
- gem.description = %q{Ruby Standard Library - tsort}
8
- gem.summary = %q{Ruby Standard Library - tsort}
9
- gem.homepage = ""
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "rubysl-tsort"
6
+ spec.version = RubySL::TSort::VERSION
7
+ spec.authors = ["Brian Shirai"]
8
+ spec.email = ["brixen@gmail.com"]
9
+ spec.description = %q{Ruby standard library tsort.}
10
+ spec.summary = %q{Ruby standard library tsort.}
11
+ spec.homepage = "https://github.com/rubysl/rubysl-tsort"
12
+ spec.license = "BSD"
10
13
 
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "rubysl-tsort"
15
- gem.require_paths = ["lib"]
16
- gem.version = RubySL::Tsort::VERSION
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
17
18
 
18
- gem.add_runtime_dependency "redcard", "~> 1.0"
19
-
20
- gem.add_development_dependency "rake", "~> 10.0"
21
- gem.add_development_dependency "mspec", "~> 1.5"
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "rake", "~> 10.0"
22
21
  end
metadata CHANGED
@@ -1,118 +1,83 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rubysl-tsort
3
- version: !ruby/object:Gem::Version
4
- hash: 856480538658449761
5
- prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Brian Shirai
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2013-04-15 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: redcard
11
+ date: 2013-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
22
21
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
26
24
  - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 4428665182548103036
29
- segments:
30
- - 1
31
- - 0
32
- version: "1.0"
33
- type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
36
28
  name: rake
37
- prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
41
31
  - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 1510892033553700768
44
- segments:
45
- - 10
46
- - 0
47
- version: "10.0"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
48
34
  type: :development
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: mspec
52
35
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
54
- none: false
55
- requirements:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
56
38
  - - ~>
57
- - !ruby/object:Gem::Version
58
- hash: 1660815245844205030
59
- segments:
60
- - 1
61
- - 5
62
- version: "1.5"
63
- type: :development
64
- version_requirements: *id003
65
- description: Ruby Standard Library - tsort
66
- email:
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Ruby standard library tsort.
42
+ email:
67
43
  - brixen@gmail.com
68
44
  executables: []
69
-
70
45
  extensions: []
71
-
72
46
  extra_rdoc_files: []
73
-
74
- files:
47
+ files:
75
48
  - .gitignore
49
+ - .travis.yml
76
50
  - Gemfile
77
51
  - LICENSE
78
52
  - README.md
79
53
  - Rakefile
80
- - lib/rubysl-tsort.rb
81
- - lib/rubysl-tsort/version.rb
54
+ - lib/rubysl/tsort.rb
55
+ - lib/rubysl/tsort/tsort.rb
56
+ - lib/rubysl/tsort/version.rb
57
+ - lib/tsort.rb
82
58
  - rubysl-tsort.gemspec
83
- homepage: ""
84
- licenses: []
85
-
59
+ homepage: https://github.com/rubysl/rubysl-tsort
60
+ licenses:
61
+ - BSD
62
+ metadata: {}
86
63
  post_install_message:
87
64
  rdoc_options: []
88
-
89
- require_paths:
65
+ require_paths:
90
66
  - lib
91
- required_ruby_version: !ruby/object:Gem::Requirement
92
- none: false
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- hash: 2002549777813010636
97
- segments:
98
- - 0
99
- version: "0"
100
- required_rubygems_version: !ruby/object:Gem::Requirement
101
- none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- hash: 2002549777813010636
106
- segments:
107
- - 0
108
- version: "0"
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
109
77
  requirements: []
110
-
111
78
  rubyforge_project:
112
- rubygems_version: 1.8.25
79
+ rubygems_version: 2.0.7
113
80
  signing_key:
114
- specification_version: 3
115
- summary: Ruby Standard Library - tsort
81
+ specification_version: 4
82
+ summary: Ruby standard library tsort.
116
83
  test_files: []
117
-
118
- has_rdoc:
data/lib/rubysl-tsort.rb DELETED
@@ -1,7 +0,0 @@
1
- require "rubysl-tsort/version"
2
-
3
- module RubySL
4
- module Tsort
5
- # Your code goes here...
6
- end
7
- end
@@ -1,5 +0,0 @@
1
- module RubySL
2
- module Tsort
3
- VERSION = "0.0.1"
4
- end
5
- end