@@ -28,13 +28,20 @@ defmodule AdventOfCode.Algorithms.DisjointSet do
28
28
}
29
29
30
30
"""
31
- @ spec new ( non_neg_integer ( ) ) :: t ( )
31
+ @ spec new ( non_neg_integer ( ) | List . t ( ) ) :: t ( )
32
32
def new ( 0 ) , do: % __MODULE__ { }
33
33
34
- def new ( size ) do
34
+ def new ( size ) when is_integer ( size ) do
35
35
% __MODULE__ {
36
- ranks: 0 .. ( size - 1 ) |> Enum . map ( & { & 1 , 1 } ) |> Enum . into ( % { } ) ,
37
- parents: 0 .. ( size - 1 ) |> Enum . map ( & { & 1 , & 1 } ) |> Enum . into ( % { } )
36
+ ranks: 0 .. ( size - 1 ) |> Map . new ( & { & 1 , 1 } ) ,
37
+ parents: 0 .. ( size - 1 ) |> Map . new ( & { & 1 , & 1 } )
38
+ }
39
+ end
40
+
41
+ def new ( lst ) when is_list ( lst ) do
42
+ % __MODULE__ {
43
+ ranks: lst |> Map . new ( & { & 1 , 1 } ) ,
44
+ parents: lst |> Map . new ( & { & 1 , & 1 } )
38
45
}
39
46
end
40
47
@@ -97,7 +104,8 @@ defmodule AdventOfCode.Algorithms.DisjointSet do
97
104
end
98
105
99
106
@ doc """
100
- Performs a union between two elements and returns the updated set.
107
+ Performs a union between two elements and returns the updated set. `:error` case is matched so that it fails
108
+ in a piped flow.
101
109
102
110
## Example
103
111
@@ -118,9 +126,12 @@ defmodule AdventOfCode.Algorithms.DisjointSet do
118
126
iex> DisjointSet.new(1) |> DisjointSet.union(100, 200)
119
127
:error
120
128
129
+ iex> DisjointSet.union(:error, 100, 200)
130
+ :error
131
+
121
132
"""
122
- @ spec union ( t ( ) , value ( ) , value ( ) ) :: t ( ) | :error
123
- def union ( disjoint_set , a , b ) do
133
+ @ spec union ( t ( ) | :error , value ( ) , value ( ) ) :: t ( ) | :error
134
+ def union ( % __MODULE__ { } = disjoint_set , a , b ) do
124
135
with { root_a , disjoint_set } <- find ( disjoint_set , a ) ,
125
136
{ root_b , disjoint_set } <- find ( disjoint_set , b ) do
126
137
union_by_rank ( disjoint_set , root_a , root_b )
@@ -129,6 +140,37 @@ defmodule AdventOfCode.Algorithms.DisjointSet do
129
140
end
130
141
end
131
142
143
+ def union ( :error , _ , _ ) , do: :error
144
+
145
+ @ doc """
146
+ Returns the connected components of a set of data. `:error` case is matched so that it fails
147
+ in a piped flow.
148
+
149
+ ## Example
150
+
151
+ iex> DisjointSet.new([{0, 0}, {0, 1}, {0, 2}, {10, 11}, {10, 12}, {100, 200}])
152
+ ...> |> DisjointSet.union({0, 0}, {0, 1})
153
+ ...> |> DisjointSet.union({0, 1}, {0, 2})
154
+ ...> |> DisjointSet.union({10, 11}, {10, 12})
155
+ ...> |> DisjointSet.components()
156
+ [MapSet.new([{0, 0}, {0, 1}, {0, 2}]), MapSet.new([{10, 11}, {10, 12}]), MapSet.new([{100, 200}])]
157
+
158
+ iex> DisjointSet.new(10)
159
+ ...> |> DisjointSet.union(20, 30)
160
+ ...> |> DisjointSet.components()
161
+ :error
162
+
163
+ """
164
+ @ spec components ( t ( ) | :error ) :: [ [ term ( ) ] ]
165
+ def components ( % __MODULE__ { parents: parents } ) do
166
+ parents
167
+ |> Enum . group_by ( & elem ( & 1 , 1 ) , fn { a , _ } -> a end )
168
+ |> Map . values ( )
169
+ |> Enum . map ( & Enum . into ( & 1 , % MapSet { } ) )
170
+ end
171
+
172
+ def components ( :error ) , do: :error
173
+
132
174
defp union_by_rank ( disjoint_set , parent , parent ) , do: disjoint_set
133
175
134
176
defp union_by_rank ( % __MODULE__ { ranks: ranks } = disjoint_set , root_a , root_b ) do
0 commit comments