jumoku 0.1.3 → 0.2.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.
- 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
|