rooted_tree 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/rooted_tree/node.rb +69 -95
- data/lib/rooted_tree/tree.rb +25 -17
- data/lib/rooted_tree/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfa5843433eb9461adb4ab0e2698e0429d994263
|
4
|
+
data.tar.gz: ef4f2f8a52ee1aa796e435cb767ab96dc7b563b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd771695cade59e40219f65827246e54534158e3d5612e8555c829b4293dafb01f2a60805cb2c36af6a8a80c1c70f5b96f7c2a616cb8af02cb568acb23e68f9e
|
7
|
+
data.tar.gz: d29de94468510ee7f2a52c2658a08e6ee8a8973d61cc1f3c6cc819f8f3751768d99fcaaae0315bc49ffe76dbcfe4b1a1e1ff41c138c798399ba0543e9f856252
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RootedTree
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/rooted_tree.png)](http://badge.fury.io/rb/rooted_tree)
|
4
4
|
[![Build Status](https://travis-ci.org/seblindberg/ruby-rooted_tree.svg?branch=master)](https://travis-ci.org/seblindberg/ruby-rooted_tree)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/github/seblindberg/ruby-rooted_tree/badge.svg?branch=master)](https://coveralls.io/github/seblindberg/ruby-rooted_tree?branch=master)
|
6
6
|
[![Inline docs](http://inch-ci.org/github/seblindberg/ruby-rooted_tree.svg?branch=master)](http://inch-ci.org/github/seblindberg/ruby-rooted_tree)
|
data/lib/rooted_tree/node.rb
CHANGED
@@ -61,16 +61,12 @@ module RootedTree
|
|
61
61
|
super
|
62
62
|
end
|
63
63
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# A node is a leaf if it has no children.
|
64
|
+
# Returns true if this node is a leaf. A leaf is a node with no children.
|
67
65
|
|
68
66
|
def leaf?
|
69
67
|
@degree == 0
|
70
68
|
end
|
71
69
|
|
72
|
-
# Internal?
|
73
|
-
#
|
74
70
|
# Returns true if the node is internal, which is equivalent to it having
|
75
71
|
# children.
|
76
72
|
|
@@ -78,17 +74,13 @@ module RootedTree
|
|
78
74
|
!leaf?
|
79
75
|
end
|
80
76
|
|
81
|
-
#
|
82
|
-
#
|
83
|
-
# Returns true if node has no parent.
|
77
|
+
# Returns true if the node has no parent.
|
84
78
|
|
85
79
|
def root?
|
86
80
|
@parent.nil?
|
87
81
|
end
|
88
82
|
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# Returns the root of the tree.
|
83
|
+
# Returns the root of the tree structure that the node is part of.
|
92
84
|
|
93
85
|
def root
|
94
86
|
return self if root?
|
@@ -98,24 +90,18 @@ module RootedTree
|
|
98
90
|
node
|
99
91
|
end
|
100
92
|
|
101
|
-
# First?
|
102
|
-
#
|
103
93
|
# Returns true if this node is the first of its siblings.
|
104
94
|
|
105
95
|
def first?
|
106
96
|
@prev.nil?
|
107
97
|
end
|
108
98
|
|
109
|
-
# Last?
|
110
|
-
#
|
111
99
|
# Returns true if this node is the last of its siblings.
|
112
100
|
|
113
101
|
def last?
|
114
102
|
@next.nil?
|
115
103
|
end
|
116
104
|
|
117
|
-
# Depth
|
118
|
-
#
|
119
105
|
# Returns the depth of the node within the tree
|
120
106
|
|
121
107
|
def depth
|
@@ -124,8 +110,6 @@ module RootedTree
|
|
124
110
|
|
125
111
|
alias level depth
|
126
112
|
|
127
|
-
# Max Depth
|
128
|
-
#
|
129
113
|
# Returns the maximum node depth under this node.
|
130
114
|
|
131
115
|
def max_depth(offset = depth)
|
@@ -134,36 +118,34 @@ module RootedTree
|
|
134
118
|
children.map { |c| c.max_depth offset + 1 }.max
|
135
119
|
end
|
136
120
|
|
137
|
-
# Max Degree
|
138
|
-
#
|
139
121
|
# Returns the highest child count of the nodes in the subtree.
|
140
122
|
|
141
123
|
def max_degree
|
142
124
|
children.map(&:degree).push(degree).max
|
143
125
|
end
|
144
126
|
|
145
|
-
# Size
|
146
|
-
#
|
147
127
|
# Calculate the size in vertecies of the subtree.
|
128
|
+
#
|
129
|
+
# Returns the number of nodes under this node, including self.
|
148
130
|
|
149
131
|
def size
|
150
132
|
children.reduce(1) { |a, e| a + e.size }
|
151
133
|
end
|
152
134
|
|
153
|
-
# Next
|
154
|
-
#
|
155
135
|
# Access the next sibling. Raises a StopIteration if this node is the last
|
156
136
|
# one.
|
137
|
+
#
|
138
|
+
# Returns the previous sibling node.
|
157
139
|
|
158
140
|
def next
|
159
141
|
raise StopIteration if last?
|
160
142
|
@next
|
161
143
|
end
|
162
144
|
|
163
|
-
# Prev(ious)
|
164
|
-
#
|
165
145
|
# Access the previous sibling. Raises a StopIteration if this node is the
|
166
146
|
# first one.
|
147
|
+
#
|
148
|
+
# Returns the previous sibling node.
|
167
149
|
|
168
150
|
def prev
|
169
151
|
raise StopIteration if first?
|
@@ -172,19 +154,19 @@ module RootedTree
|
|
172
154
|
|
173
155
|
alias previous prev
|
174
156
|
|
175
|
-
# Parent
|
176
|
-
#
|
177
157
|
# Access the parent node. Raises a StopIteration if this node is the
|
178
158
|
# root.
|
159
|
+
#
|
160
|
+
# Returns the parent node.
|
179
161
|
|
180
162
|
def parent
|
181
163
|
raise StopIteration if root?
|
182
164
|
@parent
|
183
165
|
end
|
184
166
|
|
185
|
-
# Append Sibling
|
186
|
-
#
|
187
167
|
# Insert a child between this node and the one after it.
|
168
|
+
#
|
169
|
+
# Returns self.
|
188
170
|
|
189
171
|
def append_sibling(value = nil)
|
190
172
|
raise StructureException, 'Root node can not have siblings' if root?
|
@@ -203,9 +185,9 @@ module RootedTree
|
|
203
185
|
@next = node
|
204
186
|
end
|
205
187
|
|
206
|
-
# Prepend Sibling
|
207
|
-
#
|
208
188
|
# Insert a child between this node and the one before it.
|
189
|
+
#
|
190
|
+
# Returns self.
|
209
191
|
|
210
192
|
def prepend_sibling(value = nil)
|
211
193
|
raise StructureException, 'Root node can not have siblings' if root?
|
@@ -232,9 +214,9 @@ module RootedTree
|
|
232
214
|
node.parent = self
|
233
215
|
end
|
234
216
|
|
235
|
-
# Append Child
|
236
|
-
#
|
237
217
|
# Insert a child after the last one.
|
218
|
+
#
|
219
|
+
# Returns self.
|
238
220
|
|
239
221
|
def append_child(value = nil)
|
240
222
|
if leaf?
|
@@ -247,9 +229,9 @@ module RootedTree
|
|
247
229
|
|
248
230
|
alias << append_child
|
249
231
|
|
250
|
-
# Prepend Child
|
251
|
-
#
|
252
232
|
# Insert a child before the first one.
|
233
|
+
#
|
234
|
+
# Returns self.
|
253
235
|
|
254
236
|
def prepend_child(value = nil)
|
255
237
|
if leaf?
|
@@ -259,9 +241,9 @@ module RootedTree
|
|
259
241
|
end
|
260
242
|
end
|
261
243
|
|
262
|
-
# Extract
|
263
|
-
#
|
264
244
|
# Extracts the node and its subtree from the larger structure.
|
245
|
+
#
|
246
|
+
# Returns self, now made root.
|
265
247
|
|
266
248
|
def extract
|
267
249
|
return self if root?
|
@@ -283,9 +265,9 @@ module RootedTree
|
|
283
265
|
self
|
284
266
|
end
|
285
267
|
|
286
|
-
# Delete
|
287
|
-
#
|
288
268
|
# Removes the node from the tree.
|
269
|
+
#
|
270
|
+
# Returns an array of the children to the deleted node, now made roots.
|
289
271
|
|
290
272
|
def delete
|
291
273
|
extract.children.map do |child|
|
@@ -294,12 +276,11 @@ module RootedTree
|
|
294
276
|
end
|
295
277
|
end
|
296
278
|
|
297
|
-
#
|
279
|
+
# Iterates over the nodes above this in the tree hierarchy and yields them
|
280
|
+
# to a block. If no block is given an enumerator is returned.
|
298
281
|
#
|
299
282
|
# Returns an enumerator that will iterate over the parents of this node
|
300
283
|
# until the root is reached.
|
301
|
-
#
|
302
|
-
# If a block is given it will be yielded to.
|
303
284
|
|
304
285
|
def ancestors
|
305
286
|
return to_enum(__callee__) unless block_given?
|
@@ -310,14 +291,14 @@ module RootedTree
|
|
310
291
|
end
|
311
292
|
end
|
312
293
|
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
# but by passing rtl: true the order can be reversed. If a block is not
|
317
|
-
# given an enumerator is returned.
|
294
|
+
# Yields each of the node children. The default order is left-to-right, but
|
295
|
+
# by passing rtl: true the order is reversed. If a block is not given an
|
296
|
+
# enumerator is returned.
|
318
297
|
#
|
319
298
|
# Note that the block will catch any StopIteration that is raised and
|
320
299
|
# terminate early, returning the value of the exception.
|
300
|
+
#
|
301
|
+
# rtl - reverses the iteration order if true.
|
321
302
|
|
322
303
|
def children(rtl: false)
|
323
304
|
return to_enum(__callee__, rtl: rtl) unless block_given?
|
@@ -335,9 +316,14 @@ module RootedTree
|
|
335
316
|
end
|
336
317
|
end
|
337
318
|
|
338
|
-
#
|
319
|
+
# Accessor method for any of the n children under this node. If called
|
320
|
+
# without an argument and the node has anything but exactly one child an
|
321
|
+
# exception will be raised.
|
339
322
|
#
|
340
|
-
#
|
323
|
+
# n - the n:th child to be returned. If n is negative the indexing will be
|
324
|
+
# reversed and the children counted from the last to the first.
|
325
|
+
#
|
326
|
+
# Returns the child at the n:th index.
|
341
327
|
|
342
328
|
def child(n = nil)
|
343
329
|
if n.nil?
|
@@ -362,8 +348,6 @@ module RootedTree
|
|
362
348
|
end
|
363
349
|
end
|
364
350
|
|
365
|
-
# Each
|
366
|
-
#
|
367
351
|
# Yields first to self and then to each child. If a block is not given an
|
368
352
|
# enumerator is returned.
|
369
353
|
|
@@ -373,9 +357,31 @@ module RootedTree
|
|
373
357
|
children { |v| v.each(&block) }
|
374
358
|
end
|
375
359
|
|
376
|
-
#
|
360
|
+
# Converts the tree structure to a nested array of the nodes. Each internal
|
361
|
+
# node is placed at index zero of its own array, followed by an array of its
|
362
|
+
# children. Leaf nodes are not wraped in arrays but inserted directly.
|
377
363
|
#
|
364
|
+
# flatten - flattens the array if true.
|
365
|
+
#
|
366
|
+
# Example
|
367
|
+
#
|
368
|
+
# r
|
369
|
+
# / \
|
370
|
+
# a b => [r, [[a, [c]], b]]
|
371
|
+
# |
|
372
|
+
# c
|
373
|
+
#
|
374
|
+
# Returns a nested array of nodes.
|
375
|
+
|
376
|
+
def to_a flatten: false
|
377
|
+
return super() if flatten
|
378
|
+
return self if leaf?
|
379
|
+
[self, children.map(&:to_a)]
|
380
|
+
end
|
381
|
+
|
378
382
|
# Iterates over each of the leafs.
|
383
|
+
#
|
384
|
+
# rtl - if true the iteration order is switched to right to left.
|
379
385
|
|
380
386
|
def leafs(rtl: false, &block)
|
381
387
|
return to_enum(__callee__, rtl: rtl) unless block_given?
|
@@ -383,9 +389,10 @@ module RootedTree
|
|
383
389
|
children(rtl: rtl) { |v| v.leafs(rtl: rtl, &block) }
|
384
390
|
end
|
385
391
|
|
386
|
-
#
|
392
|
+
# Iterates over each of the edges and yields the parent and the child. If no
|
393
|
+
# block is given an enumerator is returned.
|
387
394
|
#
|
388
|
-
#
|
395
|
+
# block - an optional block that will be yielded to, if given.
|
389
396
|
|
390
397
|
def edges(&block)
|
391
398
|
return to_enum(__callee__) unless block_given?
|
@@ -396,11 +403,13 @@ module RootedTree
|
|
396
403
|
end
|
397
404
|
end
|
398
405
|
|
399
|
-
# Add
|
400
|
-
#
|
401
406
|
# Add two roots together to create a larger tree. A new common root will be
|
402
407
|
# created and returned. Note that if the any of the root nodes are not
|
403
408
|
# frozen they will be modified, and as a result seize to be roots.
|
409
|
+
#
|
410
|
+
# other - a Node-like object that responds true to #root?
|
411
|
+
#
|
412
|
+
# Returns a new root with the two nodes as children.
|
404
413
|
|
405
414
|
def +(other)
|
406
415
|
unless root? && other.root?
|
@@ -414,53 +423,18 @@ module RootedTree
|
|
414
423
|
ab << a << b
|
415
424
|
end
|
416
425
|
|
417
|
-
#
|
426
|
+
# Compare one node (sub)structure with another.
|
418
427
|
#
|
419
428
|
# Returns true if the two vertecies form identical subtrees
|
420
429
|
|
421
430
|
def ==(other)
|
422
431
|
return false unless other.is_a? self.class
|
423
432
|
return false unless degree == other.degree
|
433
|
+
return false unless value == other.value
|
424
434
|
|
425
435
|
children.to_a == other.children.to_a
|
426
436
|
end
|
427
437
|
|
428
|
-
# Tree!
|
429
|
-
#
|
430
|
-
# Wraps the entire tree in a Tree object. The operation will freeze the node
|
431
|
-
# structure, making it immutable. If this node is a child the root will be
|
432
|
-
# found and passed to Tree.new.
|
433
|
-
|
434
|
-
def tree!
|
435
|
-
Tree.new root
|
436
|
-
end
|
437
|
-
|
438
|
-
# Tree
|
439
|
-
#
|
440
|
-
# Duplicates the entire tree and calls #tree! on the copy.
|
441
|
-
|
442
|
-
def tree
|
443
|
-
root.dup.tree!
|
444
|
-
end
|
445
|
-
|
446
|
-
# Subtree!
|
447
|
-
#
|
448
|
-
# Extracts this node from the larger tree and wraps it in a Tree object.
|
449
|
-
|
450
|
-
def subtree!
|
451
|
-
Tree.new extract
|
452
|
-
end
|
453
|
-
|
454
|
-
# Subtree
|
455
|
-
#
|
456
|
-
# Duplicates this node and its descendants and wraps them in a Tree object.
|
457
|
-
|
458
|
-
def subtree
|
459
|
-
Tree.new dup
|
460
|
-
end
|
461
|
-
|
462
|
-
# Inspect
|
463
|
-
#
|
464
438
|
# Visalizes the tree structure in a style very similar to the cli tool tree.
|
465
439
|
# An example of the output can be seen below. Note that the output string
|
466
440
|
# contains unicode characters.
|
data/lib/rooted_tree/tree.rb
CHANGED
@@ -1,32 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RootedTree
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(node)
|
8
|
-
@root = node.root
|
9
|
-
@root.freeze
|
10
|
-
end
|
11
|
-
|
12
|
-
def tree
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
# Degree
|
17
|
-
#
|
18
|
-
# Returns the maximum degree (number of children) in the tree.
|
4
|
+
module Tree
|
5
|
+
# Returns the maximum degree (highest number of children) in the tree.
|
19
6
|
|
20
7
|
def degree
|
21
8
|
@degree ||= root.max_degree
|
22
9
|
end
|
23
10
|
|
24
|
-
# Depth
|
25
|
-
#
|
26
11
|
# Returns the maximum depth of the tree.
|
27
12
|
|
28
13
|
def depth
|
29
14
|
@depth ||= root.max_depth
|
30
15
|
end
|
16
|
+
|
17
|
+
# Iterates over each node in the tree. When given a block it will be yielded
|
18
|
+
# to once for each node. If no block is given an enumerator is returned.
|
19
|
+
|
20
|
+
def each_node(&block)
|
21
|
+
@root.each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Iterates over each leaf in the tree. When given a block it will be yielded
|
25
|
+
# to once for leaf node. If no block is given an enumerator is returned.
|
26
|
+
|
27
|
+
def each_leaf(&block)
|
28
|
+
@root.leafs(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Iterates over each edge in the tree. An edge is composed of the parent
|
32
|
+
# node and the child, always in that order. When given a block it will be
|
33
|
+
# yielded to once for each node. If no block is given an enumerator is
|
34
|
+
# returned.
|
35
|
+
|
36
|
+
def each_edge(&block)
|
37
|
+
@root.edges(&block)
|
38
|
+
end
|
31
39
|
end
|
32
40
|
end
|
data/lib/rooted_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rooted_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Lindberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|