Skip to content

Commit c865b34

Browse files
committed
feat: add split_labelled_edge function
1 parent 460cdfd commit c865b34

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

lib/graph.ex

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,62 @@ defmodule Graph do
11171117
end
11181118
end
11191119

1120+
@doc """
1121+
Like `split_edge/4`, but requires you to specify the labelled edge to split.
1122+
1123+
## Example
1124+
iex> g = Graph.new |> Graph.add_vertices([:a, :c]) |> Graph.add_edge(:a, :c, label: :first_label, weight: 2) |> Graph.add_edge(:a, :c, label: :second_label, weight: 2)
1125+
iex> g = Graph.split_labelled_edge(g, :a, :c, :b, :first_label)
1126+
iex> Graph.edges(g)
1127+
[%Graph.Edge{v1: :a, v2: :b, label: :first_label, weight: 2}, %Graph.Edge{v1: :a, v2: :c, label: :second_label, weight: 2}, %Graph.Edge{v1: :b, v2: :c, label: :first_label, weight: 2}]
1128+
iex> Graph.split_labelled_edge(g, :a, :c, :b, :unknown_label)
1129+
{:error, :no_such_edge}
1130+
"""
1131+
@spec split_labelled_edge(t, vertex, vertex, vertex, label) :: t | {:error, :no_such_edge}
1132+
def split_labelled_edge(%__MODULE__{type: :undirected} = g, v1, v2, v3, label) do
1133+
if v1 > v2 do
1134+
do_split_labelled_edge(g, v2, v1, v3, label)
1135+
else
1136+
do_split_labelled_edge(g, v1, v2, v3, label)
1137+
end
1138+
end
1139+
1140+
def split_labelled_edge(%__MODULE__{} = g, v1, v2, v3, label) do
1141+
do_split_labelled_edge(g, v1, v2, v3, label)
1142+
end
1143+
1144+
defp do_split_labelled_edge(
1145+
%__MODULE__{out_edges: oe, edges: em, vertex_identifier: vertex_identifier} =
1146+
g,
1147+
v1,
1148+
v2,
1149+
v3,
1150+
label
1151+
) do
1152+
with v1_id <- vertex_identifier.(v1),
1153+
v2_id <- vertex_identifier.(v2),
1154+
{:ok, v1_out} <- Map.fetch(oe, v1_id),
1155+
true <- MapSet.member?(v1_out, v2_id),
1156+
meta <- Map.get(em, {v1_id, v2_id}),
1157+
true <- Enum.any?(meta, fn {edge_label, _} -> edge_label == label end) do
1158+
1159+
g = add_vertex(g, v3)
1160+
1161+
Enum.reduce(meta, g, fn {edge_label, weight}, acc ->
1162+
if edge_label == label do
1163+
acc
1164+
|> add_edge(v1, v3, label: label, weight: weight)
1165+
|> add_edge(v3, v2, label: label, weight: weight)
1166+
|> delete_edge(v1, v2, label)
1167+
else
1168+
acc
1169+
end
1170+
end)
1171+
else
1172+
_ -> {:error, :no_such_edge}
1173+
end
1174+
end
1175+
11201176
@doc """
11211177
Given two vertices, this function updates the metadata (weight/label) for the unlabelled
11221178
edge between those two vertices. If no unlabelled edge exists between them, an error

0 commit comments

Comments
 (0)