jumoku 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -2
- data/lib/jumoku.rb +36 -8
- data/lib/jumoku/builders/arborescence.rb +11 -0
- data/lib/jumoku/builders/extended.rb +327 -0
- data/lib/jumoku/builders/raw_directed_tree.rb +33 -0
- data/lib/jumoku/builders/raw_undirected_tree.rb +37 -0
- data/lib/jumoku/builders/{raw_tree.rb → shared.rb} +38 -63
- data/lib/jumoku/builders/tree.rb +5 -330
- data/lib/jumoku/classes/tree_classes.rb +6 -4
- data/lib/jumoku/ext/ext.rb +8 -2
- data/lib/jumoku/raw_tree_node.rb +5 -5
- data/lib/jumoku/support/branch.rb +9 -1
- data/lib/jumoku/support/support.rb +6 -13
- data/lib/jumoku/tree_api.rb +7 -7
- data/lib/jumoku/version.rb +2 -2
- data/spec/{raw_tree_spec.rb → raw_undirected_tree_spec.rb} +48 -86
- data/spec/tree_spec.rb +36 -48
- metadata +8 -4
@@ -1,55 +1,18 @@
|
|
1
1
|
module Jumoku
|
2
|
-
# This module provides the basic routines needed to implement the specialized
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# This module provides the basic routines needed to implement the specialized
|
3
|
+
# builders: {UndirectedTreeBuilder} and {DirectedTreeBuilder}. Those
|
4
|
+
# implementations are under the control of the {TreeAPI}.
|
5
5
|
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
# Note that a node can be any Object. There is no "node type", therefore arguments which
|
15
|
-
# are expected to be nodes are simply labelled as "`node`". A nice object type to us as
|
16
|
-
# a node would be an OpenStruct or an
|
17
|
-
# [OpenObject](http://facets.rubyforge.org/apidoc/api/more/classes/OpenObject.html)
|
18
|
-
# (from the Facets library), as it turns nodes into versatile handlers.
|
19
|
-
#
|
20
|
-
# This builder defines a few methods not required by the API so as to maintain consistency
|
21
|
-
# in the DSL.
|
22
|
-
module RawTreeBuilder
|
23
|
-
include Plexus::UndirectedGraphBuilder
|
24
|
-
|
25
|
-
# This method is called by the specialized implementations
|
26
|
-
# upon tree creation.
|
27
|
-
#
|
28
|
-
# Initialization parameters can include:
|
29
|
-
#
|
30
|
-
# * an array of branches to add
|
31
|
-
# * one or several trees to copy (will be merged if multiple)
|
32
|
-
#
|
33
|
-
# @param *params [Hash] the initialization parameters
|
34
|
-
# @return [RawTree]
|
35
|
-
def initialize(*params)
|
36
|
-
class << self
|
37
|
-
self
|
38
|
-
end.module_eval do
|
39
|
-
# Ensure the builder abides by its API requirements.
|
40
|
-
include Jumoku::TreeAPI
|
6
|
+
module Shared
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval do
|
9
|
+
# Late aliasing as it references methods provided by Plexus modules.
|
10
|
+
alias has_node? has_vertex?
|
11
|
+
alias has_branch? has_edge?
|
12
|
+
include TreeAPI
|
41
13
|
end
|
42
|
-
super(*params) # Delegates to Plexus.
|
43
14
|
end
|
44
15
|
|
45
|
-
# Ensure trees are seen as undirected graphs.
|
46
|
-
#
|
47
|
-
# @return [Boolean] false
|
48
|
-
def directed?
|
49
|
-
return false
|
50
|
-
end
|
51
|
-
# FIXME: should be able to reach Plexus's method ?!
|
52
|
-
|
53
16
|
# Adds the node to the tree.
|
54
17
|
#
|
55
18
|
# For convenience, you may pass a branch as the parameter,
|
@@ -64,7 +27,7 @@ module Jumoku
|
|
64
27
|
def add_node! u, v = nil
|
65
28
|
if nodes.empty?
|
66
29
|
add_vertex! u
|
67
|
-
elsif u.is_a?
|
30
|
+
elsif u.is_a? _branch_type
|
68
31
|
add_branch! u
|
69
32
|
elsif not v.nil?
|
70
33
|
add_branch! u, v
|
@@ -86,16 +49,17 @@ module Jumoku
|
|
86
49
|
# @overload add_branch!(b)
|
87
50
|
# @param [Branch] b Branch[node i, node j, label l = nil]; if i (j) already exists, then j (i) must not exist
|
88
51
|
# @return [RawTree] self
|
52
|
+
#
|
89
53
|
def add_branch! u, v = nil, l = nil
|
90
54
|
if has_node? u and has_node? v
|
91
55
|
unless has_branch? u, v
|
92
56
|
# Ensure the acyclic constraint.
|
93
|
-
raise
|
57
|
+
raise ForbiddenCycle, "Can't form a cycle within a tree."
|
94
58
|
end
|
95
59
|
end
|
96
60
|
|
97
61
|
# TODO: DRY this up.
|
98
|
-
if u.is_a?
|
62
|
+
if u.is_a? _branch_type
|
99
63
|
v = u.target
|
100
64
|
u = u.source
|
101
65
|
end
|
@@ -120,11 +84,11 @@ module Jumoku
|
|
120
84
|
# @param [node] u
|
121
85
|
# @return [RawTree] self
|
122
86
|
def remove_node!(u, force = false)
|
123
|
-
if terminal?
|
87
|
+
if force || terminal?(u)
|
124
88
|
remove_vertex! u
|
125
89
|
else
|
126
90
|
# Ensure the connected constraint.
|
127
|
-
raise
|
91
|
+
raise UndefinedNode, "Can't remove a non terminal node in a tree"
|
128
92
|
end
|
129
93
|
end
|
130
94
|
|
@@ -144,7 +108,7 @@ module Jumoku
|
|
144
108
|
# @param [Branch] b
|
145
109
|
# @return [RawTree] self
|
146
110
|
def remove_branch! u, v = nil
|
147
|
-
if u.is_a?
|
111
|
+
if u.is_a? _branch_type
|
148
112
|
v = u.target
|
149
113
|
u = u.source
|
150
114
|
end
|
@@ -186,14 +150,13 @@ module Jumoku
|
|
186
150
|
# @return [Array(node)] only terminal nodes (empty array if no terminal nodes,
|
187
151
|
# but should never happen in a tree).
|
188
152
|
def terminal_nodes
|
189
|
-
nodes.inject([]) do |
|
190
|
-
|
191
|
-
|
192
|
-
# (rubydoc Enumerable#inject) "at each step, memo is set to the value returned by the block"
|
193
|
-
# (sets t_nodes to nil otherwise).
|
153
|
+
nodes.inject([]) do |terminals, node|
|
154
|
+
terminals << node if terminal?(node)
|
155
|
+
terminals
|
194
156
|
end
|
195
157
|
end
|
196
158
|
alias boundaries terminal_nodes
|
159
|
+
alias leaves terminal_nodes
|
197
160
|
|
198
161
|
# The branches of the tree in a 1D array.
|
199
162
|
#
|
@@ -202,10 +165,6 @@ module Jumoku
|
|
202
165
|
edges
|
203
166
|
end
|
204
167
|
|
205
|
-
# Aliasing.
|
206
|
-
alias has_node? has_vertex?
|
207
|
-
alias has_branch? has_edge?
|
208
|
-
|
209
168
|
# Tree helpers.
|
210
169
|
|
211
170
|
# Checks whether the tree is *really* a valid tree, that is if the
|
@@ -228,10 +187,11 @@ module Jumoku
|
|
228
187
|
# root is always terminal, otherwise check whether degree is unitary
|
229
188
|
nodes == [u] ? true : (degree(u) == 1)
|
230
189
|
else
|
231
|
-
raise
|
190
|
+
raise UndefinedNode, "Not a node of this tree."
|
232
191
|
end
|
233
192
|
end
|
234
193
|
alias has_terminal_node? terminal?
|
194
|
+
alias leaf? terminal?
|
235
195
|
|
236
196
|
# Is the tree empty?
|
237
197
|
#
|
@@ -239,5 +199,20 @@ module Jumoku
|
|
239
199
|
def empty?
|
240
200
|
nodes.empty?
|
241
201
|
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
# Do *not* call cloning method on a cloned tree, as it will trigger
|
206
|
+
# an infinite cloning loop.
|
207
|
+
#
|
208
|
+
# TODO: add support for a global CLONING flag (defaults: true). If set
|
209
|
+
# to false, _clone would just return self, so that #add_node would behave
|
210
|
+
# like #add_node! for instance.
|
211
|
+
#
|
212
|
+
# @return [Tree] cloned tree
|
213
|
+
#
|
214
|
+
def _clone
|
215
|
+
self.class.new(self)
|
216
|
+
end
|
242
217
|
end
|
243
218
|
end
|
data/lib/jumoku/builders/tree.rb
CHANGED
@@ -1,336 +1,11 @@
|
|
1
1
|
module Jumoku
|
2
|
-
# This builder extends
|
3
|
-
# purpose is to implement the {TreeAPI}. {TreeBuilder} provides extended
|
4
|
-
# functionalities and acts as the main tree structure you may use, either
|
5
|
-
# by mixing-in this module or by inheritance of the associated {Tree} class.
|
2
|
+
# This builder extends {RawUndirectedTreeBuilder} implementation.
|
6
3
|
#
|
7
|
-
#
|
4
|
+
# It provides an undirected tree which acts as a hierarchical structure,
|
5
|
+
# known as a tree.
|
8
6
|
#
|
9
|
-
# # or
|
10
|
-
#
|
11
|
-
# class MyTree < Tree
|
12
|
-
# # your stuff
|
13
|
-
# end
|
14
|
-
# tree = MyTree.new
|
15
|
-
#
|
16
|
-
# # or
|
17
|
-
#
|
18
|
-
# class MyTree
|
19
|
-
# include TreeBuilder
|
20
|
-
# # your stuff
|
21
|
-
# end
|
22
|
-
# tree = MyTree.new
|
23
7
|
module TreeBuilder
|
24
|
-
include
|
25
|
-
|
26
|
-
def initialize(*params)
|
27
|
-
super(*params)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Non destructive version of {RawTreeBuilder#add_node!} (works on a copy of the tree).
|
31
|
-
#
|
32
|
-
# @overload add_node!(n)
|
33
|
-
# @param [node] n
|
34
|
-
# @overload add_node!(b)
|
35
|
-
# @param [Branch] b Branch[node i, node j, label l = nil]; if i (j) already exists, then j (i) must not exist
|
36
|
-
# @return [Tree] a modified copy of `self`
|
37
|
-
# @see RawTreeBuilder#add_node!
|
38
|
-
def add_node(n, l = nil)
|
39
|
-
x = self.class.new(self)
|
40
|
-
x.add_node!(n, l)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Non destructive version {RawTreeBuilder#add_branch!} (works on a copy of the tree).
|
44
|
-
#
|
45
|
-
# @overload add_branch!(i, j, l = nil)
|
46
|
-
# @param [node] i
|
47
|
-
# @param [node] j
|
48
|
-
# @param [Label] l
|
49
|
-
# @overload add_branch!(b)
|
50
|
-
# @param [Branch] b Branch[node i, node j, label l = nil]; if i (j) already exists, then j (i) must not exist
|
51
|
-
# @return [Tree] a modified copy of `self`
|
52
|
-
# @see RawTreeBuilder#add_branch!
|
53
|
-
def add_branch(i, j = nil, l = nil)
|
54
|
-
x = self.class.new(self)
|
55
|
-
x.add_branch!(i, j, l)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Non destructive version of {RawTreeBuilder#remove_node!} (works on a copy of the tree).
|
59
|
-
#
|
60
|
-
# @param [node] n
|
61
|
-
# @return [Tree] a modified copy of `self`
|
62
|
-
# @see RawTreeBuilder#remove_node!
|
63
|
-
def remove_node(n)
|
64
|
-
x = self.class.new(self)
|
65
|
-
x.remove_node!(n)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Non destructive version {RawTreeBuilder#remove_branch!} (works on a copy of the tree).
|
69
|
-
#
|
70
|
-
# You cannot remove non terminal branches as it would break the connectivity constraint
|
71
|
-
# of the tree.
|
72
|
-
#
|
73
|
-
# @param [node] i
|
74
|
-
# @param [node] j
|
75
|
-
# @return [Tree] a modified copy of `self`
|
76
|
-
# @see RawTreeBuilder#remove_branch!
|
77
|
-
def remove_branch(i, j = nil, *params)
|
78
|
-
x = self.class.new(self)
|
79
|
-
x.remove_branch!(i, j)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Adds all specified nodes to the node set.
|
83
|
-
#
|
84
|
-
# The nodes defines implicit branches, that is it is mandatory to provide
|
85
|
-
# an odd number of connected nodes which do not form cycles. You may specify
|
86
|
-
# Branch objects within the list, though.
|
87
|
-
#
|
88
|
-
# Valid usage:
|
89
|
-
#
|
90
|
-
# tree = Tree.new # an empty tree
|
91
|
-
# tree.add_nodes!(1,2, 2,3, 3,4) # tree.nodes => [1, 2, 3, 4]
|
92
|
-
# # branches are (1-2), (2-3), (3-4)
|
93
|
-
# tree.add_nodes!(1,2, 2,3, Branch.new(3,4), 10,1)
|
94
|
-
# tree.add_nodes! [1,2, 2,3, 3,4]
|
95
|
-
#
|
96
|
-
# Invalid usages:
|
97
|
-
#
|
98
|
-
# tree = Tree.new # an empty tree
|
99
|
-
# tree.add_nodes!(1, 2, 3) # even number of nodes
|
100
|
-
# tree.add_nodes!(1,2, 2,3, 3,1) # cycle
|
101
|
-
# tree.add_nodes!(1,2, 4,5) # not connected
|
102
|
-
#
|
103
|
-
# A helper exist to make it easy to add nodes in a suite.
|
104
|
-
# See {TreeBuilder#add_consecutive_nodes! add_consecutive_nodes!}.
|
105
|
-
#
|
106
|
-
# @param [#each] *a an Enumerable nodes set
|
107
|
-
# @return [Tree] `self`
|
108
|
-
def add_nodes!(*a)
|
109
|
-
a = a.flatten.expand_branches!
|
110
|
-
if a.size == 1 # This allows for using << to add the root as well,
|
111
|
-
if empty? # so that you may write:
|
112
|
-
add_node! a.only # Tree.new << :root << [:root, 1] << [1,2, 2,3, Branch.new(3,4)]...
|
113
|
-
else
|
114
|
-
raise RawTreeError, "Cannot add a node without a neighbour."
|
115
|
-
end
|
116
|
-
else
|
117
|
-
a.expand_branches!.each_nodes_pair { |pair| add_branch! pair[0], pair[1] }
|
118
|
-
end
|
119
|
-
self
|
120
|
-
end
|
121
|
-
alias << add_nodes!
|
122
|
-
|
123
|
-
# Same as {TreeBuilder#add_nodes! add_nodes!} but works on a copy of the receiver.
|
124
|
-
#
|
125
|
-
# @param [#each] *a an Enumerable nodes set
|
126
|
-
# @return [Tree] a modified copy of `self`
|
127
|
-
def add_nodes(*a)
|
128
|
-
x = self.class.new(self)
|
129
|
-
x.add_nodes!(*a)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Allows for adding consecutive nodes, each nodes being connected to their previous and
|
133
|
-
# next neighbours.
|
134
|
-
#
|
135
|
-
# You may pass {Branch branches} within the nodes.
|
136
|
-
#
|
137
|
-
# Tree.new.add_consecutive_nodes!(1, 2, 3, :four, Branch.new(:foo, "bar")).branches
|
138
|
-
# # => (1=2, 2=3, 3=:four, :four=:foo, :foo="bar")
|
139
|
-
#
|
140
|
-
# @param [Array(nodes)] *a flat array of unique nodes
|
141
|
-
# @return [Tree] `self`
|
142
|
-
def add_consecutive_nodes!(*a)
|
143
|
-
# FIXME: really this may not be as efficient as it could be.
|
144
|
-
# We may get rid of nodes duplication (no expand_branches!)
|
145
|
-
# and add nodes by pair, shifting one node at a time from the list.
|
146
|
-
add_nodes!(a.expand_branches!.create_nodes_pairs_list)
|
147
|
-
self
|
148
|
-
end
|
149
|
-
|
150
|
-
# Same as {TreeBuilder#add_consecutive_nodes! add_consecutive_nodes!} but
|
151
|
-
# works on a copy of the receiver.
|
152
|
-
#
|
153
|
-
# @param [Array(nodes)] *a flat array of unique nodes
|
154
|
-
# @return [Tree] a modified copy of `self`
|
155
|
-
def add_consecutive_nodes(*a)
|
156
|
-
x = self.class.new(self)
|
157
|
-
x.add_consecutive_nodes!(*a)
|
158
|
-
end
|
159
|
-
|
160
|
-
# Adds all branches mentionned in the specified Enumerable to the branch set.
|
161
|
-
#
|
162
|
-
# Elements of the Enumerable can be either two-element arrays or instances of
|
163
|
-
# {Branch}.
|
164
|
-
#
|
165
|
-
# @param [#each] *a an Enumerable branches set
|
166
|
-
# @return [Tree] `self`
|
167
|
-
def add_branches!(*a)
|
168
|
-
a.expand_branches!.each_nodes_pair { |pair| add_branch! pair[0], pair[1] }
|
169
|
-
self
|
170
|
-
end
|
171
|
-
|
172
|
-
# Same as {TreeBuilder#add_branches! add_branches!} but works on a copy of the receiver.
|
173
|
-
#
|
174
|
-
# @param [#each] *a an Enumerable branches set
|
175
|
-
# @return [Tree] a modified copy of `self`
|
176
|
-
def add_branches(*a)
|
177
|
-
x = self.class.new(self)
|
178
|
-
x.add_branches!(*a)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Removes all nodes mentionned in the specified Enumerable from the tree.
|
182
|
-
#
|
183
|
-
# The process relies on {RawTreeBuilder#remove_node! remove_node!}.
|
184
|
-
#
|
185
|
-
# @param [#each] *a an Enumerable nodes set
|
186
|
-
# @return [Tree] `self`
|
187
|
-
def remove_nodes!(*a)
|
188
|
-
a.flatten.each { |v| remove_node! v }
|
189
|
-
self
|
190
|
-
end
|
191
|
-
alias delete_nodes! remove_nodes!
|
192
|
-
|
193
|
-
# Same as {TreeBuilder#remove_nodes! remove_nodes!} but works on a copy of the receiver.
|
194
|
-
#
|
195
|
-
# @param [#each] *a a node Enumerable set
|
196
|
-
# @return [Tree] a modified copy of `self`
|
197
|
-
def remove_nodes(*a)
|
198
|
-
x = self.class.new(self)
|
199
|
-
x.remove_nodes!(*a)
|
200
|
-
end
|
201
|
-
alias delete_nodes remove_nodes
|
202
|
-
|
203
|
-
# Removes all branches mentionned in the specified Enumerable from the tree.
|
204
|
-
#
|
205
|
-
# @param [#each] *a an Enumerable branches set
|
206
|
-
# @return [Tree] `self`
|
207
|
-
def remove_branches!(*a)
|
208
|
-
a.expand_branches!.each_nodes_pair { |pair| remove_branch! pair[0], pair[1] }
|
209
|
-
self
|
210
|
-
end
|
211
|
-
alias delete_branches! remove_branches!
|
212
|
-
|
213
|
-
# Same as {TreeBuilder#remove_branches! remove_branches!} but works on a copy of the receiver.
|
214
|
-
#
|
215
|
-
# @param [#each] *a an Enumerable branches set
|
216
|
-
# @return [Tree] a modified copy of `self`
|
217
|
-
# @see RawTreeBuilder#remove_branch!
|
218
|
-
def remove_branches(*a)
|
219
|
-
x = self.class.new(self)
|
220
|
-
x.remove_branches!(*a)
|
221
|
-
end
|
222
|
-
alias delete_branches remove_branches
|
223
|
-
|
224
|
-
# Returns true if the specified node belongs to the tree.
|
225
|
-
#
|
226
|
-
# This is a default implementation that is of O(n) average complexity.
|
227
|
-
# If a subclass uses a hash to store nodes, then this can be
|
228
|
-
# made into an O(1) average complexity operation.
|
229
|
-
#
|
230
|
-
# @param [node] n
|
231
|
-
# @return [Boolean]
|
232
|
-
def node?(n)
|
233
|
-
nodes.include?(n)
|
234
|
-
end
|
235
|
-
alias has_node? node?
|
236
|
-
|
237
|
-
# Returns true if all specified nodes belong to the tree.
|
238
|
-
#
|
239
|
-
# @param [nodes]
|
240
|
-
# @return [Boolean]
|
241
|
-
def nodes?(*maybe_nodes)
|
242
|
-
maybe_nodes.all? { |node| nodes.include? node }
|
243
|
-
end
|
244
|
-
alias has_nodes? nodes?
|
245
|
-
|
246
|
-
# Returns true if any of the specified nodes belongs to the tree.
|
247
|
-
#
|
248
|
-
# @param [nodes]
|
249
|
-
# @return [Boolean]
|
250
|
-
def nodes_among?(*maybe_nodes)
|
251
|
-
maybe_nodes.any? { |node| nodes.include? node }
|
252
|
-
end
|
253
|
-
alias has_nodes_among? nodes_among?
|
254
|
-
alias has_node_among? nodes_among?
|
255
|
-
# FIXME: these helpers could be backported into Plexus.
|
256
|
-
|
257
|
-
# Returns true if i or (i,j) is a {Branch branch} of the tree.
|
258
|
-
#
|
259
|
-
# @overload branch?(a)
|
260
|
-
# @param [Branch] a
|
261
|
-
# @overload branch?(i, j)
|
262
|
-
# @param [node] i
|
263
|
-
# @param [node] j
|
264
|
-
# @return [Boolean]
|
265
|
-
def branch?(*args)
|
266
|
-
branches.include?(edge_convert(*args))
|
267
|
-
end
|
268
|
-
alias has_branch? branch?
|
269
|
-
|
270
|
-
# Returns true if the specified branches are a subset of or match exactly the
|
271
|
-
# branches set of the tree (no ordering criterion).
|
272
|
-
#
|
273
|
-
# Labels are not part of the matching constraint: only connected nodes
|
274
|
-
# matter in defining branch equality.
|
275
|
-
#
|
276
|
-
# @param [*Branch, *nodes] *maybe_branches a list of branches, either as Branch
|
277
|
-
# objects or as nodes pairs
|
278
|
-
# @return [Boolean]
|
279
|
-
def branches?(*maybe_branches)
|
280
|
-
list = maybe_branches.create_branches_list
|
281
|
-
all = true
|
282
|
-
|
283
|
-
# Branch objects are really Edge objects within Plexus, therefore
|
284
|
-
# cannot rely on #eql? to compare those structures and must drop
|
285
|
-
# down to the attributes.
|
286
|
-
list.each do |e| # Jumoku::Branch structs
|
287
|
-
all = branches.any? do |b| # Plexus::Edge structs
|
288
|
-
(b[:source] == e[:source]) and (b[:target] == e[:target])
|
289
|
-
end
|
290
|
-
end
|
291
|
-
all
|
292
|
-
end
|
293
|
-
alias has_branches? branches?
|
294
|
-
|
295
|
-
# Returns true if actual branches are included in the specified set of branches
|
296
|
-
# (no ordering criterion).
|
297
|
-
#
|
298
|
-
# Labels are not part of the matching constraint: only connected nodes
|
299
|
-
# matter in defining equality.
|
300
|
-
#
|
301
|
-
# @param [*Branch, *nodes] *maybe_branches a list of branches, either as Branch
|
302
|
-
# objects or as nodes pairs
|
303
|
-
# @return [Boolean]
|
304
|
-
def branches_among?(*maybe_branches)
|
305
|
-
list = maybe_branches.create_branches_list
|
306
|
-
all = true
|
307
|
-
|
308
|
-
# Branch objects are really Edge objects within Plexus, therefore
|
309
|
-
# cannot rely on #eql? to compare those structures and must drop
|
310
|
-
# down to the attributes.
|
311
|
-
branches.each do |e| # Plexus::Edge structs
|
312
|
-
all = list.any? do |b| # Jumoku::Branch structs
|
313
|
-
(b[:source] == e[:source]) and (b[:target] == e[:target])
|
314
|
-
end
|
315
|
-
end
|
316
|
-
all
|
317
|
-
end
|
318
|
-
alias has_branches_among? branches_among?
|
319
|
-
|
320
|
-
# Number of nodes.
|
321
|
-
#
|
322
|
-
# @return [Integer]
|
323
|
-
def num_nodes
|
324
|
-
nodes.size
|
325
|
-
end
|
326
|
-
alias number_of_nodes num_nodes
|
327
|
-
|
328
|
-
# Number of branches.
|
329
|
-
#
|
330
|
-
# @return [Integer]
|
331
|
-
def num_branches
|
332
|
-
branches.size
|
333
|
-
end
|
334
|
-
alias number_of_branches num_branches
|
8
|
+
include RawUndirectedTreeBuilder
|
9
|
+
include Extended
|
335
10
|
end
|
336
11
|
end
|