iconofthestoneage-doodl 0.0.2 → 0.0.4
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.
- data/lib/app/selfrunning.rb +185 -0
- data/lib/app/simple_app.rb +49 -0
- data/lib/app/simple_controller.rb +584 -0
- data/lib/app/simple_model.rb +292 -0
- data/lib/app/simple_view.rb +148 -0
- data/lib/breadth_first_search.rb +69 -0
- data/lib/connected_components.rb +29 -0
- data/lib/depth_first_search.rb +73 -0
- data/lib/edge.rb +57 -0
- data/lib/graph.rb +365 -0
- data/lib/graph_canvas.rb +187 -0
- data/lib/graph_generator.rb +121 -0
- data/lib/jruby/renderer.rb +413 -0
- data/lib/layout/collapse_layout.rb +23 -0
- data/lib/layout/fr_layout.rb +105 -0
- data/lib/layout/isom_layout.rb +77 -0
- data/lib/layout/kk_layout.rb +203 -0
- data/lib/layout/layout.rb +240 -0
- data/lib/layout/morph_layout.rb +65 -0
- data/lib/node.rb +57 -0
- data/lib/shortest_path/all_pair.rb +35 -0
- data/lib/shortest_path/bellman_ford.rb +60 -0
- data/lib/shortest_path/dijkstra.rb +74 -0
- data/lib/shortest_path/floyd_warshall.rb +68 -0
- data/lib/shortest_path/johnson_all_pair.rb +64 -0
- data/lib/shortest_path/single_source.rb +32 -0
- data/spec/breadth_first_search_spec.rb +145 -0
- data/spec/connected_components_spec.rb +50 -0
- data/spec/depth_first_search_spec.rb +89 -0
- data/spec/edge_spec.rb +58 -0
- data/spec/graph_generator_spec.rb +277 -0
- data/spec/graph_spec.rb +269 -0
- data/spec/jruby/renderer_spec.rb +214 -0
- data/spec/layout/layout_spec.rb +146 -0
- data/spec/node_spec.rb +179 -0
- data/spec/rspec_helper.rb +9 -0
- data/spec/rspec_suite.rb +12 -0
- data/spec/shortest_path/bellman_ford_spec.rb +101 -0
- data/spec/shortest_path/dijkstra_spec.rb +133 -0
- data/spec/shortest_path/floyd_warshall_spec.rb +84 -0
- data/spec/shortest_path/johnson_all_pair_spec.rb +90 -0
- metadata +43 -2
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "depth_first_search"
|
4
|
+
|
5
|
+
module Doodl
|
6
|
+
class ConnectedComponents
|
7
|
+
|
8
|
+
attr_reader :component
|
9
|
+
|
10
|
+
def initialize(graph, start_node = nil)
|
11
|
+
@component = {}
|
12
|
+
@component_count = 0
|
13
|
+
@dfs = DepthFirstSearch.new(graph, start_node, self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def num_connected_components
|
17
|
+
@component_count
|
18
|
+
end
|
19
|
+
|
20
|
+
def start_node(graph, node)
|
21
|
+
@component_count += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def discover(graph, node)
|
25
|
+
@component[node] = @component_count
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Doodl
|
4
|
+
|
5
|
+
class DepthFirstSearch
|
6
|
+
|
7
|
+
attr_reader :dist, :prev, :list
|
8
|
+
|
9
|
+
def initialize(graph, start_node = nil, visitor = nil, radius = nil)
|
10
|
+
@graph, @start_node, @visitor, @radius = graph, start_node, visitor, radius
|
11
|
+
@color, @prev, @dist = {}, {}, {}
|
12
|
+
@time = 0
|
13
|
+
@list = []
|
14
|
+
init_maps
|
15
|
+
main_algorithm
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def init_maps
|
21
|
+
@graph.each_node do |node|
|
22
|
+
visit(:init, @graph, node)
|
23
|
+
@color[node] = :white
|
24
|
+
@prev[node] = node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def main_algorithm
|
29
|
+
if (@start_node)
|
30
|
+
visit(:start_node, @graph, @start_node)
|
31
|
+
dfs(@start_node)
|
32
|
+
end
|
33
|
+
@graph.each_node do |node|
|
34
|
+
if @color[node] == :white
|
35
|
+
visit(:start_node, @graph, node)
|
36
|
+
dfs(node)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dfs(node)
|
42
|
+
@color[node] = :gray
|
43
|
+
visit(:discover, @graph, node)
|
44
|
+
@list << node
|
45
|
+
@dist[node] = @time
|
46
|
+
@time += 1
|
47
|
+
if (@radius == nil or @time <= @radius)
|
48
|
+
@graph.each_adjacent(node) do |adj_node, edge|
|
49
|
+
visit(:examine, @graph, edge)
|
50
|
+
if (@color[adj_node] == :white)
|
51
|
+
@prev[adj_node] = node
|
52
|
+
visit(:tree_edge, @graph, edge)
|
53
|
+
dfs(adj_node)
|
54
|
+
elsif (@color[adj_node] == :gray)
|
55
|
+
visit(:back_edge, @graph, edge)
|
56
|
+
elsif (@color[adj_node] == :black)
|
57
|
+
visit(:forward_edge, @graph, edge)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@color[node] = :black
|
61
|
+
visit(:finish, @graph, node)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def visit(position, *arg)
|
66
|
+
if @visitor.respond_to?(position)
|
67
|
+
@visitor.send(position, *arg)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/edge.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Doodl
|
4
|
+
|
5
|
+
class Edge
|
6
|
+
attr_reader :source, :target
|
7
|
+
|
8
|
+
def initialize(source, target)
|
9
|
+
@source = source
|
10
|
+
@target = target
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"(#{@source}, #{@target})"
|
15
|
+
end
|
16
|
+
|
17
|
+
def reflexive?
|
18
|
+
@source == @target
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class DirectedEdge < Edge
|
23
|
+
|
24
|
+
def ==(obj)
|
25
|
+
obj.respond_to?(:source) and
|
26
|
+
obj.respond_to?(:target) and
|
27
|
+
obj.source == self.source and
|
28
|
+
obj.target == self.target
|
29
|
+
end
|
30
|
+
|
31
|
+
def directed?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class UndirectedEdge < Edge
|
38
|
+
|
39
|
+
def ==(obj)
|
40
|
+
(obj.respond_to?(:source) and obj.respond_to?(:target)) and
|
41
|
+
((obj.source == self.source and obj.target == self.target) or
|
42
|
+
(obj.source == self.target and obj.target == self.source))
|
43
|
+
end
|
44
|
+
|
45
|
+
def eql?(obj)
|
46
|
+
(obj.respond_to?(:source) and obj.respond_to?(:target)) and
|
47
|
+
((obj.source == self.source and obj.target == self.target) or
|
48
|
+
(obj.source == self.target and obj.target == self.source))
|
49
|
+
end
|
50
|
+
|
51
|
+
def directed?
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/lib/graph.rb
ADDED
@@ -0,0 +1,365 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "node"
|
4
|
+
require "edge"
|
5
|
+
require "graph_generator"
|
6
|
+
|
7
|
+
require "forwardable"
|
8
|
+
require "observer"
|
9
|
+
|
10
|
+
module Doodl
|
11
|
+
|
12
|
+
class Graph
|
13
|
+
extend Forwardable
|
14
|
+
include Observable
|
15
|
+
include GraphGenerator
|
16
|
+
|
17
|
+
attr_reader :nodes
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@nodes = []
|
21
|
+
@node_data = Hash.new
|
22
|
+
@edge_data = Hash.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def directed?
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
#Convinience
|
29
|
+
def contains_node?(node)
|
30
|
+
@nodes.include?(node)
|
31
|
+
end
|
32
|
+
|
33
|
+
def contains_edge?(edge)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_edge(source, target)
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
#Attachable User Data
|
42
|
+
def attach_node_data(key, hash = Hash.new)
|
43
|
+
@node_data[key] = hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def detach_node_data(key)
|
47
|
+
@node_data.delete(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
def attach_edge_data(key, hash = Hash.new)
|
51
|
+
@edge_data[key] = hash
|
52
|
+
end
|
53
|
+
|
54
|
+
def detach_edge_data(key)
|
55
|
+
@edge_data.delete(key)
|
56
|
+
end
|
57
|
+
|
58
|
+
def_delegator(:@node_data, :keys, :node_data_keys)
|
59
|
+
|
60
|
+
def_delegator(:@node_data, :[], :node_data)
|
61
|
+
|
62
|
+
def_delegator(:@node_data, :has_key?, :has_node_data_key?)
|
63
|
+
|
64
|
+
def_delegator(:@edge_data, :keys, :edge_data_keys)
|
65
|
+
|
66
|
+
def_delegator(:@edge_data, :[], :edge_data)
|
67
|
+
|
68
|
+
def_delegator(:@edge_data, :has_key?, :has_edge_data_key?)
|
69
|
+
|
70
|
+
#Mutable Graph
|
71
|
+
private
|
72
|
+
def gen_node
|
73
|
+
raise NotImplementedError
|
74
|
+
end
|
75
|
+
|
76
|
+
def gen_edge(source, target)
|
77
|
+
raise NotImplementedError
|
78
|
+
end
|
79
|
+
|
80
|
+
public
|
81
|
+
def add_node(update = false)
|
82
|
+
node = self.gen_node
|
83
|
+
@nodes << node
|
84
|
+
changed if update
|
85
|
+
notify_observers(self)
|
86
|
+
return node
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_edge(source, target)
|
90
|
+
raise NotImplementedError
|
91
|
+
end
|
92
|
+
|
93
|
+
def del_node(node, update = false)
|
94
|
+
raise NotImplementedError
|
95
|
+
end
|
96
|
+
|
97
|
+
def del_edge(edge, update = false)
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
|
101
|
+
#Node List Graph
|
102
|
+
def_delegator(:@nodes, :each, :each_node)
|
103
|
+
|
104
|
+
def_delegator(:@nodes, :each_with_index, :each_node_with_index)
|
105
|
+
|
106
|
+
def each_node_passing(condition)
|
107
|
+
if block_given?
|
108
|
+
each_node do |node|
|
109
|
+
yield(node) if (condition.call(node))
|
110
|
+
end
|
111
|
+
else
|
112
|
+
raise ArgumentError, "No block given."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def_delegator(:@nodes, :size, :num_nodes)
|
117
|
+
|
118
|
+
#Edge List Graph
|
119
|
+
def each_edge
|
120
|
+
raise NotImplementedError
|
121
|
+
end
|
122
|
+
|
123
|
+
def num_edges
|
124
|
+
@nodes.inject(0) { |memo, node| memo + node.out_degree }
|
125
|
+
end
|
126
|
+
|
127
|
+
def source(edge)
|
128
|
+
edge.source
|
129
|
+
end
|
130
|
+
|
131
|
+
def target(edge)
|
132
|
+
edge.target
|
133
|
+
end
|
134
|
+
|
135
|
+
#Incidence
|
136
|
+
def out_degree(node)
|
137
|
+
node.degree
|
138
|
+
end
|
139
|
+
|
140
|
+
def each_adjacent_edge(node)
|
141
|
+
raise NotImplementedError
|
142
|
+
end
|
143
|
+
|
144
|
+
#Adjacent Graph
|
145
|
+
def each_adjacent_node(node)
|
146
|
+
raise NotImplementedError
|
147
|
+
end
|
148
|
+
|
149
|
+
def to_s
|
150
|
+
nodes = @nodes.inject("") { |memo, o| memo << o.to_s << ", " }
|
151
|
+
edges = ""
|
152
|
+
self.each_edge { |e| edges << e.to_s << ", "}
|
153
|
+
str = ""
|
154
|
+
str << "Graph Type: #{self.class.name}\n"
|
155
|
+
str << "Nodes: [#{nodes.chomp(", ")}]\n"
|
156
|
+
str << "Edges: [#{edges.chomp(", ")}]\n"
|
157
|
+
str << "Attached Node Data Keys: #{@node_data.keys.inspect}\n" if @node_data
|
158
|
+
str << "Attached Edge Data Keys: #{@edge_data.keys.inspect}\n" if @edge_data
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
class DirectedGraph < Graph
|
164
|
+
|
165
|
+
def directed?
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
def includes_edge?(edge)
|
170
|
+
raise ArgumentError unless edge.respond_to?(:source) and edge.respond_to?(:target)
|
171
|
+
contains_node?(edge.source) and contains_node?(edge.target) and edge.source.includes_out_edge?(edge)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def gen_node
|
176
|
+
DirectedNode.new
|
177
|
+
end
|
178
|
+
|
179
|
+
def gen_edge(source, target)
|
180
|
+
DirectedEdge.new(source, target)
|
181
|
+
end
|
182
|
+
|
183
|
+
def get_edge(source, target)
|
184
|
+
raise ArgumentError, "Source and Target must be member of Graph" unless (@nodes.include?(source) and @nodes.include?(target))
|
185
|
+
source.get_edge(target)
|
186
|
+
end
|
187
|
+
|
188
|
+
def del_node(node, update = false)
|
189
|
+
each_edge do |edge|
|
190
|
+
if (edge.source == node or edge.target == node)
|
191
|
+
source = edge.source
|
192
|
+
source.del_out_edge(edge)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
@nodes.delete(node)
|
196
|
+
changed if update
|
197
|
+
notify_observers(self)
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_edge(source, target, update = false)
|
201
|
+
raise ArgumentError, "Source/Target must be member of graph" unless (@nodes.include?(source) and @nodes.include?(target))
|
202
|
+
edge = DirectedEdge.new(source, target)
|
203
|
+
raise ArgumentError if source.includes_out_edge?(edge)
|
204
|
+
source.add_out_edge(edge)
|
205
|
+
changed if update
|
206
|
+
notify_observers(self)
|
207
|
+
return edge
|
208
|
+
end
|
209
|
+
|
210
|
+
def del_edge(edge, update = false)
|
211
|
+
edge.source.del_out_edge(edge)
|
212
|
+
changed if update
|
213
|
+
notify_observers(self)
|
214
|
+
end
|
215
|
+
|
216
|
+
def each_edge
|
217
|
+
each_node do |source|
|
218
|
+
source.each_out_edge { |edge| yield(edge) }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
#Incidence
|
223
|
+
def out_degree(node)
|
224
|
+
node.out_degree
|
225
|
+
end
|
226
|
+
|
227
|
+
def each_adjacent_edge(node)
|
228
|
+
node.each_out_edge { |edge| yield(edge) }
|
229
|
+
end
|
230
|
+
|
231
|
+
def each_adjacent_node(node)
|
232
|
+
each_adjacent_edge(node) { |edge| yield(target(edge)) }
|
233
|
+
end
|
234
|
+
|
235
|
+
def each_adjacent(node)
|
236
|
+
each_adjacent_edge(node) { |edge| yield(target(edge), edge) }
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
class SimpleBidirectionalGraph < DirectedGraph
|
242
|
+
|
243
|
+
def gen_node
|
244
|
+
SimpleBidirectionalNode.new
|
245
|
+
end
|
246
|
+
|
247
|
+
def add_edge(source, target, update = false)
|
248
|
+
raise ArgumentError unless (@nodes.include?(source) and @nodes.include?(target))
|
249
|
+
edge = DirectedEdge.new(source, target)
|
250
|
+
raise ArgumentError if (source.includes_out_edge(edge) or target.includes_in_edge?(edge))
|
251
|
+
source.add_out_edge(edge)
|
252
|
+
target.add_in_edge(edge)
|
253
|
+
changed if update
|
254
|
+
notify_observers(self)
|
255
|
+
return edge
|
256
|
+
end
|
257
|
+
|
258
|
+
def each_in_edge(node)
|
259
|
+
node.each_in_edge do |edge|
|
260
|
+
yield(edge)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
class UndirectedGraph < Graph
|
267
|
+
|
268
|
+
def directed?
|
269
|
+
false
|
270
|
+
end
|
271
|
+
|
272
|
+
def includes_edge?(edge)
|
273
|
+
edge.source.includes_edge?(edge)
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
def gen_node
|
278
|
+
UndirectedNode.new
|
279
|
+
end
|
280
|
+
|
281
|
+
def get_edge(source, target)
|
282
|
+
raise ArgumentError, "Source and Target must be member of Graph" unless
|
283
|
+
(@nodes.include?(source) and @nodes.include?(target))
|
284
|
+
source.get_edge(target)
|
285
|
+
end
|
286
|
+
|
287
|
+
def gen_edge(source, target)
|
288
|
+
UndirectedEdge.new(source, target)
|
289
|
+
end
|
290
|
+
|
291
|
+
def del_node(node, update = false)
|
292
|
+
each_edge do |edge|
|
293
|
+
if (edge.source == node or edge.target == node)
|
294
|
+
edge.source.del_edge(edge)
|
295
|
+
edge.target.del_edge(edge)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
@nodes.delete(node)
|
299
|
+
changed if update
|
300
|
+
notify_observers(self)
|
301
|
+
end
|
302
|
+
|
303
|
+
def add_edge(source, target, update = false)
|
304
|
+
raise ArgumentError unless (@nodes.include?(source) and @nodes.include?(target))
|
305
|
+
edge = UndirectedEdge.new(source, target)
|
306
|
+
raise ArgumentError if (source.includes_edge?(edge) or target.includes_edge?(edge))
|
307
|
+
source.add_edge(edge)
|
308
|
+
target.add_edge(edge)
|
309
|
+
changed if update
|
310
|
+
notify_observers(self)
|
311
|
+
return edge
|
312
|
+
end
|
313
|
+
|
314
|
+
def del_edge(edge, update = false)
|
315
|
+
edge.source.del_edge(edge)
|
316
|
+
edge.target.del_edge(edge)
|
317
|
+
changed if update
|
318
|
+
notify_observers(self)
|
319
|
+
end
|
320
|
+
|
321
|
+
def each_edge
|
322
|
+
result = []
|
323
|
+
each_node do |source|
|
324
|
+
each_adjacent_edge(source) do |edge|
|
325
|
+
result << edge
|
326
|
+
end
|
327
|
+
end
|
328
|
+
result.uniq.each { |edge| yield(edge) }
|
329
|
+
end
|
330
|
+
|
331
|
+
def each_adjacent_edge(node)
|
332
|
+
node.each_edge { |edge| yield(edge) }
|
333
|
+
end
|
334
|
+
|
335
|
+
def each_adjacent_node(node)
|
336
|
+
each_adjacent_edge(node) do |edge|
|
337
|
+
if target(edge) == node
|
338
|
+
yield(source(edge))
|
339
|
+
else
|
340
|
+
yield(target(edge))
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def each_adjacent(node)
|
346
|
+
each_adjacent_edge(node) do |edge|
|
347
|
+
if target(edge) == node
|
348
|
+
yield(source(edge), edge)
|
349
|
+
else
|
350
|
+
yield(target(edge), edge)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def num_edges
|
356
|
+
num = 0
|
357
|
+
each_node do |node|
|
358
|
+
num += node.degree
|
359
|
+
end
|
360
|
+
return num / 2
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|