plexus 0.5.4 → 0.5.5

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.
Files changed (70) hide show
  1. data/Gemfile +3 -0
  2. data/LICENSE +37 -0
  3. data/README.md +208 -0
  4. data/Rakefile +25 -0
  5. data/lib/plexus.rb +90 -0
  6. data/lib/plexus/adjacency_graph.rb +225 -0
  7. data/lib/plexus/arc.rb +60 -0
  8. data/lib/plexus/arc_number.rb +50 -0
  9. data/lib/plexus/biconnected.rb +84 -0
  10. data/lib/plexus/chinese_postman.rb +91 -0
  11. data/lib/plexus/classes/graph_classes.rb +28 -0
  12. data/lib/plexus/common.rb +63 -0
  13. data/lib/plexus/comparability.rb +63 -0
  14. data/lib/plexus/directed_graph.rb +78 -0
  15. data/lib/plexus/directed_graph/algorithms.rb +95 -0
  16. data/lib/plexus/directed_graph/distance.rb +167 -0
  17. data/lib/plexus/dot.rb +94 -0
  18. data/lib/plexus/edge.rb +38 -0
  19. data/lib/plexus/ext.rb +79 -0
  20. data/lib/plexus/graph.rb +628 -0
  21. data/lib/plexus/graph_api.rb +35 -0
  22. data/lib/plexus/labels.rb +112 -0
  23. data/lib/plexus/maximum_flow.rb +77 -0
  24. data/lib/plexus/ruby_compatibility.rb +17 -0
  25. data/lib/plexus/search.rb +510 -0
  26. data/lib/plexus/strong_components.rb +93 -0
  27. data/lib/plexus/support/support.rb +9 -0
  28. data/lib/plexus/undirected_graph.rb +56 -0
  29. data/lib/plexus/undirected_graph/algorithms.rb +90 -0
  30. data/lib/plexus/version.rb +6 -0
  31. data/spec/biconnected_spec.rb +27 -0
  32. data/spec/chinese_postman_spec.rb +27 -0
  33. data/spec/community_spec.rb +44 -0
  34. data/spec/complement_spec.rb +27 -0
  35. data/spec/digraph_distance_spec.rb +121 -0
  36. data/spec/digraph_spec.rb +339 -0
  37. data/spec/dot_spec.rb +48 -0
  38. data/spec/edge_spec.rb +158 -0
  39. data/spec/inspection_spec.rb +38 -0
  40. data/spec/multi_edge_spec.rb +32 -0
  41. data/spec/neighborhood_spec.rb +36 -0
  42. data/spec/properties_spec.rb +146 -0
  43. data/spec/search_spec.rb +227 -0
  44. data/spec/spec.opts +4 -0
  45. data/spec/spec_helper.rb +59 -0
  46. data/spec/strong_components_spec.rb +61 -0
  47. data/spec/triangulated_spec.rb +125 -0
  48. data/spec/undirected_graph_spec.rb +220 -0
  49. data/vendor/priority-queue/CHANGELOG +33 -0
  50. data/vendor/priority-queue/Makefile +140 -0
  51. data/vendor/priority-queue/README +133 -0
  52. data/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
  53. data/vendor/priority-queue/compare_comments.rb +49 -0
  54. data/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  55. data/vendor/priority-queue/doc/compare_big.gp +14 -0
  56. data/vendor/priority-queue/doc/compare_big.png +0 -0
  57. data/vendor/priority-queue/doc/compare_small.gp +15 -0
  58. data/vendor/priority-queue/doc/compare_small.png +0 -0
  59. data/vendor/priority-queue/doc/results.csv +37 -0
  60. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  61. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
  62. data/vendor/priority-queue/lib/priority_queue.rb +14 -0
  63. data/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  64. data/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  65. data/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
  66. data/vendor/priority-queue/priority_queue.so +0 -0
  67. data/vendor/priority-queue/setup.rb +1551 -0
  68. data/vendor/priority-queue/test/priority_queue_test.rb +371 -0
  69. data/vendor/rdot.rb +360 -0
  70. metadata +100 -10
@@ -0,0 +1,14 @@
1
+ # A priority queue implementation.
2
+ # This extension contains two implementations, a c extension and a pure ruby
3
+ # implementation. When the compiled extension can not be found, it falls back
4
+ # to the pure ruby extension.
5
+ #
6
+ # See CPriorityQueue and RubyPriorityQueue for more information.
7
+
8
+ begin
9
+ require 'priority_queue/CPriorityQueue'
10
+ PriorityQueue = CPriorityQueue
11
+ rescue LoadError # C Version could not be found, try ruby version
12
+ require 'priority_queue/ruby_priority_queue'
13
+ PriorityQueue = RubyPriorityQueue
14
+ end
@@ -0,0 +1 @@
1
+ require "priority_queue/CPriorityQueue"
@@ -0,0 +1,46 @@
1
+ # A Poor mans Priority Queue. (Very inefficent but minimal implemention).
2
+ class PoorPriorityQueue < Hash
3
+ def push(object, priority)
4
+ self[object] = priority
5
+ end
6
+
7
+ def min
8
+ return nil if self.empty?
9
+ min_k = self.keys.first
10
+ min_p = self[min_k]
11
+ self.each do | k, p |
12
+ min_k, min_p = k, p if p < min_p
13
+ end
14
+ [min_k, min_p]
15
+ end
16
+
17
+ def min_key
18
+ min[0] rescue nil
19
+ end
20
+
21
+ def min_priority
22
+ min[1] rescue nil
23
+ end
24
+
25
+ def delete_min
26
+ return nil if self.empty?
27
+ min_k, min_p = *min
28
+ self.delete(min_k)
29
+ [min_k, min_p]
30
+ end
31
+
32
+ def delete_min_return_key
33
+ delete_min[0] rescue nil
34
+ end
35
+
36
+ def delete_min_return_priority
37
+ delete_min[1] rescue nil
38
+ end
39
+
40
+ def delete(object)
41
+ return nil unless self.has_key?(object)
42
+ result = [object, self[object]]
43
+ super
44
+ result
45
+ end
46
+ end
@@ -0,0 +1,526 @@
1
+ # Pure ruby Priority Queue
2
+ class RubyPriorityQueue
3
+
4
+ include Enumerable
5
+
6
+ private
7
+
8
+ #
9
+ def link_nodes(b1, b2)
10
+ return link_nodes(b2, b1) if b2.priority < b1.priority
11
+
12
+ b2.parent = b1
13
+ child = b1.child
14
+ b1.child = b2
15
+ if child
16
+ b2.left = child.left
17
+ b2.left.right = b2
18
+ b2.right = child
19
+ child.left = b2
20
+ else
21
+ b2.left = b2
22
+ b2.right = b2
23
+ end
24
+ b1.degree += 1
25
+ b2.mark = false # TODO: Check if this is correct, or if b1 should be marked as false
26
+ return b1
27
+ end
28
+
29
+ # Does not change length
30
+ def delete_first
31
+ return nil unless @rootlist
32
+
33
+ result = @rootlist
34
+ if result == result.right
35
+ @min = @rootlist = nil
36
+ else
37
+ @rootlist = result.right
38
+ @rootlist.left = result.left
39
+ @rootlist.left.right = @rootlist
40
+
41
+ result.right = result.left = result
42
+ end
43
+ return result;
44
+ end
45
+
46
+ def cut_node(n)
47
+ return self unless n.parent
48
+ n.parent.degree -= 1
49
+ if n.parent.child == n
50
+ if n.right == n
51
+ n.parent.child = nil
52
+ else
53
+ n.parent.child = n.right;
54
+ end
55
+ end
56
+ n.parent = nil
57
+ n.right.left = n.left
58
+ n.left.right = n.right
59
+
60
+ n.right = @rootlist
61
+ n.left = @rootlist.left
62
+ @rootlist.left.right = n
63
+ @rootlist.left = n
64
+
65
+ n.mark = false
66
+
67
+ return self
68
+ end
69
+
70
+ # Does not change length
71
+ def insert_tree(tree)
72
+ if @rootlist == nil
73
+ @rootlist = @min = tree
74
+ else
75
+ l = @rootlist.left
76
+ l.right = tree
77
+ @rootlist.left = tree
78
+ tree.left = l
79
+ tree.right = @rootlist
80
+ @min = tree if tree.priority < @min.priority
81
+ end
82
+ self
83
+ end
84
+
85
+ def consolidate
86
+ return self if self.empty?
87
+ array_size = (2.0 * Math.log(self.length) / Math.log(2) + 1.0).ceil
88
+ tree_by_degree = Array.new(array_size)
89
+
90
+ while n = delete_first
91
+ while n1 = tree_by_degree[n.degree]
92
+ tree_by_degree[n.degree] = nil;
93
+ n = link_nodes(n, n1);
94
+ end
95
+ tree_by_degree[n.degree] = n;
96
+ end
97
+
98
+ @rootlist = @min = nil;
99
+ tree_by_degree.each do | tree |
100
+ next unless tree
101
+ insert_tree(tree)
102
+ end
103
+ self
104
+ end
105
+
106
+ # Node class used internally
107
+ class Node # :nodoc:
108
+ attr_accessor :parent, :left, :right, :key, :priority, :degree, :mark
109
+ attr_reader :child
110
+
111
+ def child=(c)
112
+ raise "Circular Child" if c == self
113
+ raise "Child is neighbour" if c == self.right
114
+ raise "Child is neighbour" if c == self.left
115
+ @child = c
116
+ end
117
+
118
+ def to_dot(only_down = false, known_nodes = [])
119
+ p known_nodes.map { | n | n.dot_id }
120
+ p self.dot_id
121
+ result = []
122
+ if only_down
123
+ raise "Circular #{caller.inspect}" if known_nodes.include?(self)
124
+ known_nodes << self
125
+
126
+ result << "#{dot_id} [label=\"#{@key}: #{@priority}\"];"
127
+ l = " "
128
+ #l << "#{@left.dot_id} <- #{dot_id}; " if @left
129
+ l << "#{dot_id} -> #{@left.dot_id} [constraint=false]; " if @left and @left.dot_id < self.dot_id
130
+ l << "#{dot_id} -> #{@right.dot_id} [constraint=false];\t\t\t\t/*neighbours*/" if @right and @right.dot_id <= self.dot_id
131
+ result << l
132
+ result << " #{dot_id} -> #{@child.dot_id}; //child" if @child
133
+ result << @child.to_dot(false, known_nodes) if @child
134
+ else
135
+ n = self
136
+ begin
137
+ result.concat(n.to_dot(true, known_nodes))
138
+ n = n.right
139
+ end while n != self
140
+ end
141
+ result.flatten.map{|r| " " << r}
142
+ end
143
+
144
+ def dot_id
145
+ "N#{@key}"
146
+ end
147
+
148
+ def initialize(key, priority)
149
+ @key = key; @priority = priority; @degree = 0
150
+ end
151
+ end
152
+
153
+ public
154
+
155
+ # Returns the number of elements of the queue.
156
+ #
157
+ # q = PriorityQueue.new
158
+ # q.length #=> 0
159
+ # q[0] = 1
160
+ # q.length #=> 1
161
+ attr_reader :length
162
+
163
+ # Create a new, empty PriorityQueue
164
+ def initialize
165
+ @nodes = Hash.new
166
+ @rootlist = nil
167
+ @min = nil
168
+ @length = 0
169
+ end
170
+
171
+ # Print a priority queue as a dot-graph. The output can be fed to dot from the
172
+ # vizgraph suite to create a tree depicting the internal datastructure.
173
+ def to_dot
174
+ r = ["digraph fibheap {"]
175
+ #r << @rootlist.to_dot.join("\n") if @rootlist
176
+ r << "ROOT -> #{@rootlist.dot_id};" if @rootlist
177
+ @nodes.to_a.sort.each do | (_, n) |
178
+ r << " #{n.dot_id} [label=\"#{n.key}: #{n.priority}\"];"
179
+ r << " #{n.dot_id} -> #{n.right.dot_id} [constraint=false];" if n.right# and n.dot_id < n.right.dot_id
180
+ r << " #{n.dot_id} -> #{n.left.dot_id} [constraint=false];" if n.left #and n.dot_id < n.left.dot_id
181
+ r << " #{n.dot_id} -> #{n.child.dot_id}" if n.child
182
+ end
183
+ r << "}"
184
+ r.join("\n")
185
+ r
186
+ end
187
+
188
+ # Call dot and gv displaying the datstructure
189
+ def display_dot
190
+ puts to_dot
191
+ system "echo '#{to_dot}' | twopi -Tps -Groot=ROOT -Goverlap=false> /tmp/dotfile.ps; gv /tmp/dotfile.ps"
192
+ end
193
+
194
+ # call-seq:
195
+ # [key] = priority
196
+ # change_priority(key, priority)
197
+ # push(key, priority)
198
+ #
199
+ # Set the priority of a key.
200
+ #
201
+ # q = PriorityQueue.new
202
+ # q["car"] = 50
203
+ # q["train"] = 50
204
+ # q["bike"] = 10
205
+ # q.min #=> ["bike", 10]
206
+ # q["car"] = 0
207
+ # q.min #=> ["car", 0]
208
+ def change_priority(key, priority)
209
+ return push(key, priority) unless @nodes[key]
210
+
211
+ n = @nodes[key]
212
+ if n.priority < priority # Priority was increased. Remove the node and reinsert.
213
+ self.delete(key)
214
+ self.push(key, priority);
215
+ return self
216
+ end
217
+ n.priority = priority;
218
+ @min = n if n.priority < @min.priority
219
+
220
+ return self if !n.parent or n.parent.priority <= n.priority # Already in rootlist or bigger than parent
221
+ begin # Cascading Cuts
222
+ p = n.parent
223
+ cut_node(n)
224
+ n = p
225
+ end while n.mark and n.parent
226
+ n.mark = true if n.parent
227
+
228
+ self
229
+ end
230
+
231
+ # Add an object to the queue.
232
+ def push(key, priority)
233
+ return change_priority(key, priority) if @nodes[key]
234
+ @nodes[key] = node = Node.new(key, priority)
235
+ @min = node if !@min or priority < @min.priority
236
+ if not @rootlist
237
+ @rootlist = node
238
+ node.left = node.right = node
239
+ else
240
+ node.left = @rootlist.left
241
+ node.right = @rootlist
242
+ @rootlist.left.right = node
243
+ @rootlist.left = node
244
+ end
245
+ @length += 1
246
+ self
247
+ end
248
+
249
+ # Returns true if the array is empty, false otherwise.
250
+ def empty?
251
+ @rootlist.nil?
252
+ end
253
+
254
+ # call-seq:
255
+ # [key] -> priority
256
+ #
257
+ # Return the priority of a key or nil if the key is not in the queue.
258
+ #
259
+ # q = PriorityQueue.new
260
+ # (0..10).each do | i | q[i.to_s] = i end
261
+ # q["5"] #=> 5
262
+ # q[5] #=> nil
263
+ def [](key)
264
+ @nodes[key] and @nodes[key].priority
265
+ end
266
+
267
+ # call-seq:
268
+ # has_key? key -> boolean
269
+ #
270
+ # Return false if the key is not in the queue, true otherwise.
271
+ #
272
+ # q = PriorityQueue.new
273
+ # (0..10).each do | i | q[i.to_s] = i end
274
+ # q.has_key("5") #=> true
275
+ # q.has_key(5) #=> false
276
+ def has_key?(key)
277
+ @nodes.has_key?(key)
278
+ end
279
+
280
+ alias :[]= :push
281
+
282
+ # Call the given block with each [key, priority] pair in the queue
283
+ #
284
+ # Beware: Changing the queue in the block may lead to unwanted behaviour and
285
+ # even infinite loops.
286
+ def each
287
+ @nodes.each do | key, node |
288
+ yield(key, node.priority)
289
+ end
290
+ end
291
+
292
+ # call-seq:
293
+ # min -> [object, priority]
294
+ #
295
+ # Return the pair [object, priority] with minimal priority or nil when the
296
+ # queue is empty.
297
+ #
298
+ # q = PriorityQueue.new
299
+ # q["a"] = 10
300
+ # q["b"] = 20
301
+ # q.min #=> ["a", 10]
302
+ # q.delete_min #=> ["a", 10]
303
+ # q.min #=> ["b", 20]
304
+ # q.delete_min #=> ["b", 20]
305
+ # q.min #=> nil
306
+ def min
307
+ [@min.key, @min.priority] rescue nil
308
+ end
309
+
310
+ # call-seq:
311
+ # min_key -> object
312
+ #
313
+ # Return the key that has the minimal priority or nil when the queue is empty.
314
+ #
315
+ # q = PriorityQueue.new
316
+ # q["a"] = 10
317
+ # q["b"] = 20
318
+ # q.min_key #=> "a"
319
+ # q.delete_min #=> ["a", 10]
320
+ # q.min_key #=> "b"
321
+ # q.delete_min #=> ["b", 20]
322
+ # q.min_key #=> nil
323
+ def min_key
324
+ @min.key rescue nil
325
+ end
326
+
327
+ # call-seq:
328
+ # min_priority -> priority
329
+ #
330
+ # Return the minimal priority or nil when the queue is empty.
331
+ #
332
+ # q = PriorityQueue.new
333
+ # q["a"] = 10
334
+ # q["b"] = 20
335
+ # q.min_priority #=> 10
336
+ # q.delete_min #=> ["a", 10]
337
+ # q.min_priority #=> 20
338
+ # q.delete_min #=> ["b", 20]
339
+ # q.min_priority #=> nil
340
+ def min_priority
341
+ @min.priority rescue nil
342
+ end
343
+
344
+ # call-seq:
345
+ # delete(key) -> [key, priority]
346
+ # delete(key) -> nil
347
+ #
348
+ # Delete a key from the priority queue. Returns nil when the key was not in
349
+ # the queue and [key, priority] otherwise.
350
+ #
351
+ # q = PriorityQueue.new
352
+ # (0..10).each do | i | q[i.to_s] = i end
353
+ # q.delete(5) #=> ["5", 5]
354
+ # q.delete(5) #=> nil
355
+ def delete(key)
356
+ return nil unless n = @nodes.delete(key)
357
+
358
+ if n.child
359
+ c = n.child
360
+ e = n.child
361
+ begin
362
+ r = c.right
363
+ cut_node(c)
364
+ c = r
365
+ end while c != e
366
+ end
367
+ cut_node(n) if n.parent
368
+
369
+ if n == n.right
370
+ @min = nil;
371
+ @rootlist = nil;
372
+ else
373
+ @rootlist = n.right if @rootlist == n
374
+ if @min == n
375
+ n1 = n.right
376
+ @min = n1
377
+ begin
378
+ @min = n1 if n1.priority < @min.priority
379
+ n1 = n1.right
380
+ end while(n1 != n);
381
+ end
382
+ n.right.left = n.left
383
+ n.left.right = n.right
384
+ n.left = n
385
+ n.right = n
386
+ end
387
+ @length -= 1
388
+ return [n.key, n.priority]
389
+ end
390
+
391
+ # call-seq:
392
+ # delete_min_return_key -> key
393
+ #
394
+ # Delete key with minimal priority and return the key
395
+ #
396
+ # q = PriorityQueue.new
397
+ # q["a"] = 1
398
+ # q["b"] = 0
399
+ # q.delete_min_return_key #=> "b"
400
+ # q.delete_min_return_key #=> "a"
401
+ # q.delete_min_return_key #=> nil
402
+ def delete_min_return_key
403
+ delete_min[0] rescue nil
404
+ end
405
+
406
+ # call-seq:
407
+ # delete_min_return_priority -> priority
408
+ #
409
+ # Delete key with minimal priority and return the priority value
410
+ #
411
+ # q = PriorityQueue.new
412
+ # q["a"] = 1
413
+ # q["b"] = 0
414
+ # q.delete_min_return_priority #=> 0
415
+ # q.delete_min_return_priority #=> 1
416
+ # q.delete_min_return_priority #=> nil
417
+ def delete_min_return_priority
418
+ delete_min[1] rescue nil
419
+ end
420
+
421
+ # call-seq:
422
+ # delete_min -> [key, priority]
423
+ #
424
+ # Delete key with minimal priority and return [key, priority]
425
+ #
426
+ # q = PriorityQueue.new
427
+ # q["a"] = 1
428
+ # q["b"] = 0
429
+ # q.delete_min #=> ["b", 0]
430
+ # q.delete_min #=> ["a", 1]
431
+ # q.delete_min #=> nil
432
+ def delete_min
433
+ return nil if self.empty?
434
+ result = self.min
435
+
436
+ @nodes.delete(@min.key)
437
+
438
+ if @length == 1
439
+ @rootlist = @min = nil
440
+ @length = 0
441
+ else
442
+ min = @min
443
+ if @min == @rootlist # If the rootlist is anchored at the minimum, shift to the right
444
+ if @rootlist == @rootlist.right
445
+ @rootlist = @min = nil
446
+ else
447
+ @rootlist = @min = @min.right
448
+ end
449
+ end
450
+ min.left.right = min.right;
451
+ min.right.left = min.left;
452
+ min.left = min.right = min;
453
+ if min.child
454
+ # Kinder und Eltern trennen, Markierung aufheben
455
+ n = min.child;
456
+ begin
457
+ n.parent = nil;
458
+ n.mark = false;
459
+ n = n.right;
460
+ end while n != min.child
461
+
462
+ # Kinder einf�gen
463
+ if @rootlist
464
+ l1 = @rootlist.left
465
+ l2 = n.left
466
+
467
+ l1.right = n
468
+ n.left = l1
469
+ l2.right = @rootlist
470
+ @rootlist.left = l2
471
+ else
472
+ @rootlist = n
473
+ end
474
+ end
475
+
476
+ # Gr��e anpassen
477
+ @length -= 1
478
+
479
+ # Wieder aufh�bschen
480
+ consolidate
481
+ end
482
+
483
+ result
484
+ end
485
+
486
+ # Returns a string representation of the priority queue.
487
+ def inspect
488
+ "<PriorityQueue: #{@nodes.map{|(_, n)| [n.key, n.priority]}.sort_by{|(_,p)|p}.inspect}>"
489
+ end
490
+
491
+ def initialize_copy(copy)
492
+ copy_nodes = @nodes
493
+ @nodes = {}
494
+
495
+ copy_nodes.each do | (_, cn) |
496
+ n = @nodes[cn.key] = Node.new(cn.key, cn.priority)
497
+ n.mark = cn.mark
498
+ n.degree = cn.degree
499
+ end
500
+
501
+ copy_nodes.each do | (_, cn) |
502
+ n = @nodes[cn.key]
503
+ n.left = @nodes[cn.left.key] if cn.left
504
+ n.right = @nodes[cn.right.key] if cn.right
505
+ n.parent = @nodes[cn.parent.key] if cn.parent
506
+ n.child = @nodes[cn.child.key] if cn.child
507
+ end
508
+ @rootlist = @nodes[@rootlist.key] if @rootlist
509
+ @min = @nodes[@min.key] if @min
510
+ self
511
+ end
512
+ end
513
+
514
+ if __FILE__ == $0
515
+ q = RubyPriorityQueue.new
516
+
517
+ ('a'..'z').each do | n |
518
+ q[n] = n[0]
519
+ end
520
+ q.delete_min
521
+ q.delete_min
522
+ q.delete_min
523
+ q.delete_min
524
+ q.display_dot
525
+ q.delete_min
526
+ end