Skip to content

Commit

Permalink
feat: add split_labelled_edge function
Browse files Browse the repository at this point in the history
  • Loading branch information
leolaudouard committed Mar 15, 2024
1 parent 460cdfd commit 076ad6f
Showing 1 changed file with 40 additions and 23 deletions.
63 changes: 40 additions & 23 deletions lib/graph.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1070,47 +1070,64 @@ defmodule Graph do
...> g = Graph.split_edge(g, :a, :c, :b)
...> Graph.edges(g)
[%Graph.Edge{v1: :a, v2: :b, weight: 2}, %Graph.Edge{v1: :b, v2: :c, weight: 2}]
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)
...> g = Graph.split_edge(g, :a, :c, :b)
...> Graph.edges(g)
[%Graph.Edge{v1: :a, v2: :b, label: :first_label, weight: 2}, %Graph.Edge{v1: :a, v2: :b, label: :second_label, weight: 2}, %Graph.Edge{v1: :b, v2: :c, label: :first_label, weight: 2}, %Graph.Edge{v1: :b, v2: :c, label: :second_label, weight: 2}]
"""
@spec split_edge(t, vertex, vertex, vertex) :: t | {:error, :no_such_edge}
def split_edge(%__MODULE__{type: :undirected} = g, v1, v2, v3) do
def split_edge(%__MODULE__{} = g, v1, v2, v3) do
split_labelled_edge(g, v1, v2, v3, nil)
end

@doc """
Like `split_edge/4`, but requires you to specify the labelled edge to split.
Th implementation of `splt_edge/4` is actually `split_labelled_edge(g, v1, v2, v3, nil)`.
## Example
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)
...> g = Graph.split_labelled_edge(g, :a, :c, :b, :first_label)
...> Graph.edges(g)
[%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}]
"""
def split_labelled_edge(%__MODULE__{type: :undirected} = g, v1, v2, v3, label) do
if v1 > v2 do
do_split_edge(g, v2, v1, v3)
do_split_labelled_edge(g, v2, v1, v3, label)
else
do_split_edge(g, v1, v2, v3)
do_split_labelled_edge(g, v1, v2, v3, label)
end
end

def split_edge(%__MODULE__{} = g, v1, v2, v3) do
do_split_edge(g, v1, v2, v3)
def split_labelled_edge(%__MODULE__{} = g, v1, v2, v3, label) do
do_split_labelled_edge(g, v1, v2, v3, label)
end

defp do_split_edge(
%__MODULE__{in_edges: ie, out_edges: oe, edges: em, vertex_identifier: vertex_identifier} =
defp do_split_labelled_edge(
%__MODULE__{out_edges: oe, edges: em, vertex_identifier: vertex_identifier} =
g,
v1,
v2,
v3
v3,
label
) do
with v1_id <- vertex_identifier.(v1),
v2_id <- vertex_identifier.(v2),
{:ok, v1_out} <- Map.fetch(oe, v1_id),
{:ok, v2_in} <- Map.fetch(ie, v2_id),
true <- MapSet.member?(v1_out, v2_id),
meta <- Map.get(em, {v1_id, v2_id}),
v1_out <- MapSet.delete(v1_out, v2_id),
v2_in <- MapSet.delete(v2_in, v1_id) do
g = %__MODULE__{
g
| in_edges: Map.put(ie, v2_id, v2_in),
out_edges: Map.put(oe, v1_id, v1_out)
}

meta <- Map.get(em, {v1_id, v2_id}) do
g = add_vertex(g, v3)

Enum.reduce(meta, g, fn {label, weight}, acc ->
acc
|> add_edge(v1, v3, label: label, weight: weight)
|> add_edge(v3, v2, label: label, weight: weight)
Enum.reduce(meta, g, fn {edge_label, weight}, acc ->
if edge_label == label or label == nil do
acc
|> add_edge(v1, v3, label: edge_label, weight: weight)
|> add_edge(v3, v2, label: edge_label, weight: weight)
|> delete_edge(v1, v2, edge_label)
else
acc
end
end)
else
_ -> {:error, :no_such_edge}
Expand Down

0 comments on commit 076ad6f

Please sign in to comment.