tangle 0.5.1 → 0.6.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 +32 -2
- data/lib/tangle/directed/edge.rb +7 -5
- data/lib/tangle/directed/graph.rb +0 -4
- data/lib/tangle/edge.rb +21 -8
- data/lib/tangle/graph.rb +39 -94
- data/lib/tangle/graph_private.rb +30 -0
- data/lib/tangle/graph_protected.rb +45 -0
- data/lib/tangle/mixin.rb +11 -1
- data/lib/tangle/simple/graph.rb +4 -3
- data/lib/tangle/vertex.rb +16 -20
- data/tangle.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecbc32a01243a5579254a7beca7175df838759511ca5af297668ec54ab245ff2
|
4
|
+
data.tar.gz: b437ade2b5f953312b845a3d9fd5679d6e40b7c98a437b5ce6677f866824de9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8af8b8acc6f0400e03eb760a3baffe12987b5f797bd952cf9a271312163433b1129670056991dd5febe959c74fa894e99a048e22e470c48241f140eff1b4836
|
7
|
+
data.tar.gz: c455890c8be488f6d38d83f003f731a5db70426464f14c85107344d277297446c73225e8ca615d556e8bacf079934bd5ff9724a60716a7212485dcbae870e2f8
|
data/README.md
CHANGED
@@ -2,19 +2,21 @@
|
|
2
2
|
|
3
3
|
# Tangle
|
4
4
|
|
5
|
-
Tangle aims to untangle your graphs, by providing a set of classes for managing different types of graphs, and mixins for adding specific feature sets
|
5
|
+
Tangle aims to untangle your graphs, by providing a set of classes for managing different types of graphs, and mixins for adding specific feature sets.
|
6
6
|
|
7
7
|
**Graph types**:
|
8
8
|
* SimpleGraph
|
9
9
|
* MultiGraph
|
10
10
|
* DiGraph
|
11
|
-
*
|
11
|
+
* DAG
|
12
12
|
* ~~Tree~~
|
13
13
|
|
14
14
|
**Feature mixins**:
|
15
15
|
* Connectedness
|
16
16
|
* Ancestry
|
17
|
+
* ~~Vertex ordering~~
|
17
18
|
* ~~Coloring~~
|
19
|
+
* ~~GraphViz~~
|
18
20
|
|
19
21
|
## Installation
|
20
22
|
|
@@ -51,6 +53,34 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
51
53
|
|
52
54
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
53
55
|
|
56
|
+
### Mixin API
|
57
|
+
|
58
|
+
A mixin is a module with optional submodules for each of the classes
|
59
|
+
`Graph`, `Vertex`, and `Edge`. If the mixin needs initial state it
|
60
|
+
should provide a keyword initializer `#initialize_kwarg_KEYWORD(argument)`,
|
61
|
+
that will be called when the object is `#initalize`d with a matching kwarg.
|
62
|
+
|
63
|
+
Example:
|
64
|
+
```ruby
|
65
|
+
module WeightedEdges
|
66
|
+
module Edge
|
67
|
+
def initialize_kwarg_weight(weight)
|
68
|
+
@weight = weight
|
69
|
+
end
|
70
|
+
|
71
|
+
def weight
|
72
|
+
@weight
|
73
|
+
end
|
74
|
+
|
75
|
+
def weight=(new_weight)
|
76
|
+
@weight = new_weight
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Tangle::Graph.new(mixins: [WeightedEdges])
|
82
|
+
```
|
83
|
+
|
54
84
|
## Contributing
|
55
85
|
|
56
86
|
Bug reports and pull requests are welcome on GitHub at https://github.com/notCalle/tangle. Pull requests should be rebased to HEAD of `master` before submitting, and all commits must be signed with valid GPG key. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/lib/tangle/directed/edge.rb
CHANGED
@@ -6,11 +6,6 @@ module Tangle
|
|
6
6
|
# An edge in a directed graph
|
7
7
|
#
|
8
8
|
class Edge < Tangle::Edge
|
9
|
-
def initialize(vertex1, vertex2 = vertex1, graph: nil)
|
10
|
-
@child, @parent = @vertices = [vertex1, vertex2]
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
9
|
def parent?(vertex)
|
15
10
|
@parent == vertex
|
16
11
|
end
|
@@ -26,6 +21,13 @@ module Tangle
|
|
26
21
|
def child(_vertex = nil)
|
27
22
|
@child
|
28
23
|
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def with_vertices(vertex1, vertex2 = vertex1)
|
28
|
+
@child, @parent = @vertices = [vertex1, vertex2]
|
29
|
+
self
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
data/lib/tangle/edge.rb
CHANGED
@@ -16,11 +16,11 @@ module Tangle
|
|
16
16
|
#
|
17
17
|
# End users should probably use Graph#add_edge instead.
|
18
18
|
#
|
19
|
-
def initialize(vertex1, vertex2 = vertex1, graph: nil)
|
20
|
-
|
21
|
-
|
19
|
+
def initialize(vertex1, vertex2 = vertex1, graph: nil, **kwargs)
|
20
|
+
with_graph(graph)
|
21
|
+
with_vertices(vertex1, vertex2)
|
22
22
|
|
23
|
-
initialize_mixins
|
23
|
+
initialize_mixins(**kwargs)
|
24
24
|
|
25
25
|
validate_edge
|
26
26
|
end
|
@@ -41,22 +41,23 @@ module Tangle
|
|
41
41
|
@vertices.find { |other| other != for_vertex } || for_vertex
|
42
42
|
end
|
43
43
|
|
44
|
-
#
|
44
|
+
# Clone an edge into another graph, replacing original vertices with
|
45
45
|
# their already prepared duplicates in the other graph. Returns nil if any
|
46
46
|
# of the vertices does not exist in the other graph.
|
47
47
|
# End users should probably use Graph#subgraph instead.
|
48
48
|
#
|
49
|
-
#
|
49
|
+
# clone_into(graph) => Edge or nil
|
50
50
|
#
|
51
51
|
# Raises an ArgumentError if graph would remain the same.
|
52
52
|
#
|
53
|
-
def
|
53
|
+
def clone_into(graph)
|
54
54
|
raise ArgumentError if graph == @graph
|
55
55
|
|
56
56
|
vertices = @vertices.map do |vertex|
|
57
57
|
graph.get_vertex(vertex.vertex_id)
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
|
+
clone.with_graph(graph).with_vertices(*vertices)
|
60
61
|
rescue KeyError
|
61
62
|
nil
|
62
63
|
end
|
@@ -78,6 +79,18 @@ module Tangle
|
|
78
79
|
|
79
80
|
def_delegators :@vertices, :include?, :hash
|
80
81
|
|
82
|
+
protected
|
83
|
+
|
84
|
+
def with_graph(graph)
|
85
|
+
@graph = graph
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def with_vertices(vertex1, vertex2 = vertex1)
|
90
|
+
@vertices = Set[vertex1, vertex2]
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
81
94
|
private
|
82
95
|
|
83
96
|
def validate_edge
|
data/lib/tangle/graph.rb
CHANGED
@@ -1,32 +1,46 @@
|
|
1
1
|
require 'tangle/mixin'
|
2
2
|
require 'tangle/vertex'
|
3
3
|
require 'tangle/edge'
|
4
|
+
require 'tangle/graph_private'
|
5
|
+
require 'tangle/graph_protected'
|
4
6
|
|
5
7
|
module Tangle
|
6
8
|
#
|
7
9
|
# Base class for all kinds of graphs
|
8
10
|
#
|
9
11
|
class Graph
|
12
|
+
include Tangle::GraphPrivate
|
13
|
+
include Tangle::GraphProtected
|
10
14
|
include Tangle::Mixin::Initialize
|
11
15
|
Edge = Tangle::Edge
|
16
|
+
DEFAULT_MIXINS = [Tangle::Mixin::Connectedness].freeze
|
12
17
|
|
13
|
-
# Initialize a new graph,
|
14
|
-
#
|
15
|
-
# Graph.new() => Graph
|
16
|
-
# Graph.new(vertices: +array_or_hash+) => Graph
|
17
|
-
# Graph.new(vertices: +array_or_hash+, edges: +array_or_hash+) => Graph
|
18
|
-
# Graph.new(mixins: [MixinModule, ...], ...) => Graph
|
18
|
+
# Initialize a new graph, preloading it with vertices and edges
|
19
19
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# get assigned unique names (within the graph).
|
20
|
+
# Graph[+vertices+] => Graph
|
21
|
+
# Graph[+vertices+, +edges+) => Graph
|
23
22
|
#
|
24
|
-
# +vertices+
|
25
|
-
#
|
23
|
+
# When +vertices+ is a hash, it contains initialization kwargs as
|
24
|
+
# values and vertex names as keys. When +vertices+ is an array of
|
25
|
+
# initialization kwargs, the vertices will be be anonymous.
|
26
26
|
#
|
27
27
|
# +edges+ can contain an array of exactly two, either names of vertices
|
28
28
|
# or vertices.
|
29
29
|
#
|
30
|
+
# Any kwarg supported by Graph.new is also allowed.
|
31
|
+
#
|
32
|
+
def self.[](vertices, edges = {}, **kwargs)
|
33
|
+
graph = new(**kwargs)
|
34
|
+
graph.add_vertices(vertices)
|
35
|
+
edges.each { |from, to| graph.add_edge(from, to) }
|
36
|
+
graph
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initialize a new graph, optionally preloading it with vertices and edges
|
40
|
+
#
|
41
|
+
# Graph.new() => Graph
|
42
|
+
# Graph.new(mixins: [MixinModule, ...], ...) => Graph
|
43
|
+
#
|
30
44
|
# +mixins+ is an array of modules that can be mixed into the various
|
31
45
|
# classes that makes up a graph. Initialization of a Graph, Vertex or Edge
|
32
46
|
# looks for submodules in each mixin, with the same name and extends
|
@@ -35,15 +49,10 @@ module Tangle
|
|
35
49
|
# Any subclass of Graph should also subclass Edge to manage its unique
|
36
50
|
# constraints.
|
37
51
|
#
|
38
|
-
def initialize(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@edges ||= []
|
43
|
-
|
44
|
-
initialize_mixins(mixins)
|
45
|
-
initialize_vertices(vertices)
|
46
|
-
initialize_edges(edges)
|
52
|
+
def initialize(mixins: self.class::DEFAULT_MIXINS, **kwargs)
|
53
|
+
initialize_mixins(mixins, **kwargs)
|
54
|
+
initialize_vertices
|
55
|
+
initialize_edges
|
47
56
|
end
|
48
57
|
|
49
58
|
# Get all edges.
|
@@ -85,12 +94,20 @@ module Tangle
|
|
85
94
|
#
|
86
95
|
# Optional named arguments:
|
87
96
|
# name: unique name or label for vertex
|
88
|
-
# contents: delegate object for missing methods
|
89
97
|
#
|
90
98
|
def add_vertex(**kvargs)
|
91
99
|
insert_vertex(Vertex.new(graph: self, **kvargs))
|
92
100
|
end
|
93
101
|
|
102
|
+
def add_vertices(vertices)
|
103
|
+
case vertices
|
104
|
+
when Hash
|
105
|
+
vertices.each { |name, kwargs| add_vertex(name: name, **kwargs) }
|
106
|
+
else
|
107
|
+
vertices.each { |kwargs| add_vertex(**kwargs) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
94
111
|
def get_vertex(name_or_vertex)
|
95
112
|
case name_or_vertex
|
96
113
|
when Vertex
|
@@ -109,81 +126,9 @@ module Tangle
|
|
109
126
|
# Unless a selector is provided, the subgraph contains the entire graph.
|
110
127
|
#
|
111
128
|
def subgraph(&selector)
|
112
|
-
|
113
|
-
|
114
|
-
dup_vertices_into(graph, &selector)
|
115
|
-
dup_edges_into(graph)
|
116
|
-
|
117
|
-
graph
|
129
|
+
clone.with_vertices(vertices(&selector)).with_edges(edges)
|
118
130
|
end
|
119
|
-
alias dup subgraph
|
120
131
|
|
121
132
|
attr_reader :mixins
|
122
|
-
|
123
|
-
protected
|
124
|
-
|
125
|
-
# Insert a prepared vertex into the graph
|
126
|
-
#
|
127
|
-
def insert_vertex(vertex)
|
128
|
-
raise ArgumentError unless vertex.graph.eql?(self)
|
129
|
-
|
130
|
-
@vertices_by_name[vertex.name] = vertex unless vertex.name.nil?
|
131
|
-
@vertices_by_id[vertex.vertex_id] = vertex
|
132
|
-
end
|
133
|
-
|
134
|
-
# Insert a prepared edge into the graph
|
135
|
-
#
|
136
|
-
def insert_edge(edge)
|
137
|
-
raise ArgumentError unless edge.graph.eql?(self)
|
138
|
-
|
139
|
-
@edges << edge
|
140
|
-
edge
|
141
|
-
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def initialize_vertices(vertices)
|
146
|
-
return if vertices.nil?
|
147
|
-
|
148
|
-
case vertices
|
149
|
-
when Hash
|
150
|
-
initialize_named_vertices(vertices)
|
151
|
-
else
|
152
|
-
initialize_anonymous_vertices(vertices)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def initialize_named_vertices(vertices)
|
157
|
-
vertices.each do |name, delegate|
|
158
|
-
add_vertex(name: name, delegate: delegate)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def initialize_anonymous_vertices(vertices)
|
163
|
-
vertices.each do |delegate|
|
164
|
-
add_vertex(delegate: delegate)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def initialize_edges(edges)
|
169
|
-
return if edges.nil?
|
170
|
-
|
171
|
-
edges.each do |vertices|
|
172
|
-
add_edge(*vertices)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def dup_vertices_into(graph, &selector)
|
177
|
-
vertices(&selector).each do |vertex|
|
178
|
-
graph.insert_vertex(vertex.dup_into(graph))
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def dup_edges_into(graph)
|
183
|
-
edges.each do |edge|
|
184
|
-
new_edge = edge.dup_into(graph)
|
185
|
-
graph.insert_edge(new_edge) unless new_edge.nil?
|
186
|
-
end
|
187
|
-
end
|
188
133
|
end
|
189
134
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Tangle
|
2
|
+
#
|
3
|
+
# The private bits of Graph
|
4
|
+
#
|
5
|
+
module GraphPrivate
|
6
|
+
private
|
7
|
+
|
8
|
+
def initialize_vertices
|
9
|
+
@vertices_by_id = {}
|
10
|
+
@vertices_by_name = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize_edges
|
14
|
+
@edges = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def clone_vertices_into(graph, &selector)
|
18
|
+
vertices(&selector).each do |vertex|
|
19
|
+
graph.insert_vertex(vertex.clone_into(graph))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def clone_edges_into(graph)
|
24
|
+
edges.each do |edge|
|
25
|
+
new_edge = edge.clone_into(graph)
|
26
|
+
graph.insert_edge(new_edge) unless new_edge.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Tangle
|
2
|
+
#
|
3
|
+
# The protected bits of Graph
|
4
|
+
#
|
5
|
+
module GraphProtected
|
6
|
+
protected
|
7
|
+
|
8
|
+
# Insert a prepared vertex into the graph
|
9
|
+
#
|
10
|
+
def insert_vertex(vertex)
|
11
|
+
raise ArgumentError unless vertex.graph.eql?(self)
|
12
|
+
|
13
|
+
@vertices_by_name[vertex.name] = vertex unless vertex.name.nil?
|
14
|
+
@vertices_by_id[vertex.vertex_id] = vertex
|
15
|
+
end
|
16
|
+
|
17
|
+
# Insert a prepared edge into the graph
|
18
|
+
#
|
19
|
+
def insert_edge(edge)
|
20
|
+
raise ArgumentError unless edge.graph.eql?(self)
|
21
|
+
|
22
|
+
@edges << edge
|
23
|
+
edge
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_vertices(vertices = [])
|
27
|
+
initialize_vertices
|
28
|
+
|
29
|
+
vertices.each do |vertex|
|
30
|
+
insert_vertex(vertex.clone_into(self))
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_edges(edges = [])
|
36
|
+
initialize_edges
|
37
|
+
|
38
|
+
edges.each do |edge|
|
39
|
+
new_edge = edge.clone_into(self)
|
40
|
+
insert_edge(new_edge) unless new_edge.nil?
|
41
|
+
end
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/tangle/mixin.rb
CHANGED
@@ -9,7 +9,7 @@ module Tangle
|
|
9
9
|
module Initialize
|
10
10
|
private
|
11
11
|
|
12
|
-
def initialize_mixins(mixins = nil)
|
12
|
+
def initialize_mixins(mixins = nil, **kwargs)
|
13
13
|
case klass = self.class.name[/[^:]+$/].to_sym
|
14
14
|
when :Graph
|
15
15
|
@mixins = mixins
|
@@ -18,6 +18,7 @@ module Tangle
|
|
18
18
|
end
|
19
19
|
|
20
20
|
extend_with_mixins(klass, mixins) unless mixins.nil?
|
21
|
+
initialize_kwargs(**kwargs) unless kwargs.empty?
|
21
22
|
end
|
22
23
|
|
23
24
|
def extend_with_mixins(klass, mixins)
|
@@ -25,6 +26,15 @@ module Tangle
|
|
25
26
|
extend(mixin.const_get(klass)) if mixin.const_defined?(klass)
|
26
27
|
end
|
27
28
|
end
|
29
|
+
|
30
|
+
def initialize_kwargs(**kwargs)
|
31
|
+
kwargs.each do |keyword, argument|
|
32
|
+
initializer = "initialize_kwarg_#{keyword}".to_sym
|
33
|
+
can_init = respond_to?(initializer)
|
34
|
+
raise ArgumentError, "unknown keyword: #{keyword}" unless can_init
|
35
|
+
send initializer, argument
|
36
|
+
end
|
37
|
+
end
|
28
38
|
end
|
29
39
|
end
|
30
40
|
end
|
data/lib/tangle/simple/graph.rb
CHANGED
data/lib/tangle/vertex.rb
CHANGED
@@ -6,8 +6,7 @@ module Tangle
|
|
6
6
|
#
|
7
7
|
# A named vertex in a graph
|
8
8
|
#
|
9
|
-
class Vertex
|
10
|
-
include PP::ObjectMixin
|
9
|
+
class Vertex
|
11
10
|
include Tangle::Mixin::Initialize
|
12
11
|
|
13
12
|
# Create a new vertex
|
@@ -17,38 +16,29 @@ module Tangle
|
|
17
16
|
# Named arguments:
|
18
17
|
# graph: a Graph or nil for an orphaned vertex
|
19
18
|
# name: anything that's hashable and unique within the graph
|
20
|
-
# delegate: delegate object for missing methods
|
21
19
|
#
|
22
20
|
def initialize(graph: nil,
|
23
21
|
name: nil,
|
24
|
-
|
25
|
-
|
26
|
-
super(delegate) unless delegate.nil?
|
27
|
-
|
22
|
+
vertex_id: object_id,
|
23
|
+
**kwargs)
|
28
24
|
@graph = graph
|
29
25
|
@name = name
|
30
|
-
@delegate = delegate
|
31
26
|
@vertex_id = vertex_id
|
32
27
|
|
33
|
-
initialize_mixins
|
28
|
+
initialize_mixins(**kwargs)
|
34
29
|
end
|
35
30
|
|
36
|
-
#
|
37
|
-
# End users should probably use Graph#
|
31
|
+
# Clone a vertex in a new graph, keeping all other contained attributes
|
32
|
+
# End users should probably use Graph#subgraph instead.
|
38
33
|
#
|
39
|
-
#
|
34
|
+
# clone_into(new_graph) => Vertex
|
40
35
|
#
|
41
36
|
# Raises an ArgumentError if graph would remain the same.
|
42
37
|
#
|
43
|
-
def
|
38
|
+
def clone_into(graph)
|
44
39
|
raise ArgumentError if graph == @graph
|
45
40
|
|
46
|
-
|
47
|
-
graph: graph,
|
48
|
-
name: @name,
|
49
|
-
delegate: @delegate,
|
50
|
-
vertex_id: @vertex_id
|
51
|
-
)
|
41
|
+
clone.with_graph(graph)
|
52
42
|
end
|
53
43
|
|
54
44
|
# Return all edges that touch this vertex
|
@@ -92,7 +82,13 @@ module Tangle
|
|
92
82
|
|
93
83
|
attr_reader :graph
|
94
84
|
attr_reader :name
|
95
|
-
attr_reader :delegate
|
96
85
|
attr_reader :vertex_id
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def with_graph(graph)
|
90
|
+
@graph = graph
|
91
|
+
self
|
92
|
+
end
|
97
93
|
end
|
98
94
|
end
|
data/tangle.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tangle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Calle Englund
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git-version-bump
|
@@ -106,6 +106,8 @@ files:
|
|
106
106
|
- lib/tangle/edge.rb
|
107
107
|
- lib/tangle/errors.rb
|
108
108
|
- lib/tangle/graph.rb
|
109
|
+
- lib/tangle/graph_private.rb
|
110
|
+
- lib/tangle/graph_protected.rb
|
109
111
|
- lib/tangle/mixin.rb
|
110
112
|
- lib/tangle/mixin/ancestry.rb
|
111
113
|
- lib/tangle/mixin/connectedness.rb
|