Current section
Files
Jump to
Current section
Files
src/yog@model.erl
-module(yog@model).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog/model.gleam").
-export([new/1, add_node/3, successors/2, predecessors/2, neighbors/2, all_nodes/1, successor_ids/2, add_edge/4]).
-export_type([graph_type/0, graph/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type graph_type() :: directed | undirected.
-type graph(DTH, DTI) :: {graph,
graph_type(),
gleam@dict:dict(integer(), DTH),
gleam@dict:dict(integer(), gleam@dict:dict(integer(), DTI)),
gleam@dict:dict(integer(), gleam@dict:dict(integer(), DTI))}.
-file("src/yog/model.gleam", 38).
?DOC(
" Creates a new empty graph of the specified type.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let graph = model.new(Directed)\n"
" ```\n"
).
-spec new(graph_type()) -> graph(any(), any()).
new(Graph_type) ->
{graph, Graph_type, maps:new(), maps:new(), maps:new()}.
-file("src/yog/model.gleam", 57).
?DOC(
" Adds a node to the graph with the given ID and data.\n"
" If a node with this ID already exists, its data will be replaced.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" graph\n"
" |> model.add_node(1, \"Node A\")\n"
" |> model.add_node(2, \"Node B\")\n"
" ```\n"
).
-spec add_node(graph(DTN, DTO), integer(), DTN) -> graph(DTN, DTO).
add_node(Graph, Id, Data) ->
New_nodes = gleam@dict:insert(erlang:element(3, Graph), Id, Data),
{graph,
erlang:element(2, Graph),
New_nodes,
erlang:element(4, Graph),
erlang:element(5, Graph)}.
-file("src/yog/model.gleam", 88).
?DOC(" Gets nodes you can travel TO (Successors).\n").
-spec successors(graph(any(), DUA), integer()) -> list({integer(), DUA}).
successors(Graph, Id) ->
_pipe = erlang:element(4, Graph),
_pipe@1 = gleam_stdlib:map_get(_pipe, Id),
_pipe@2 = gleam@result:map(_pipe@1, fun maps:to_list/1),
gleam@result:unwrap(_pipe@2, []).
-file("src/yog/model.gleam", 96).
?DOC(" Gets nodes you came FROM (Predecessors).\n").
-spec predecessors(graph(any(), DUF), integer()) -> list({integer(), DUF}).
predecessors(Graph, Id) ->
_pipe = erlang:element(5, Graph),
_pipe@1 = gleam_stdlib:map_get(_pipe, Id),
_pipe@2 = gleam@result:map(_pipe@1, fun maps:to_list/1),
gleam@result:unwrap(_pipe@2, []).
-file("src/yog/model.gleam", 105).
?DOC(
" Gets everyone connected to the node, regardless of direction.\n"
" Useful for algorithms like finding \"connected components.\"\n"
).
-spec neighbors(graph(any(), DUK), integer()) -> list({integer(), DUK}).
neighbors(Graph, Id) ->
case erlang:element(2, Graph) of
undirected ->
successors(Graph, Id);
directed ->
Out = successors(Graph, Id),
In_ = predecessors(Graph, Id),
gleam@list:fold(
In_,
Out,
fun(Acc, Incoming) ->
{In_id, _} = Incoming,
case gleam@list:any(
Out,
fun(O) -> erlang:element(1, O) =:= In_id end
) of
true ->
Acc;
false ->
[Incoming | Acc]
end
end
)
end.
-file("src/yog/model.gleam", 126).
?DOC(
" Returns all node IDs in the graph.\n"
" This includes all nodes, even isolated nodes with no edges.\n"
).
-spec all_nodes(graph(any(), any())) -> list(integer()).
all_nodes(Graph) ->
maps:keys(erlang:element(3, Graph)).
-file("src/yog/model.gleam", 132).
?DOC(
" Returns just the NodeIds of successors (without edge weights).\n"
" Convenient for traversal algorithms that only need the IDs.\n"
).
-spec successor_ids(graph(any(), any()), integer()) -> list(integer()).
successor_ids(Graph, Id) ->
_pipe = successors(Graph, Id),
gleam@list:map(_pipe, fun(Edge) -> erlang:element(1, Edge) end).
-file("src/yog/model.gleam", 137).
-spec do_add_directed_edge(graph(DUY, DUZ), integer(), integer(), DUZ) -> graph(DUY, DUZ).
do_add_directed_edge(Graph, Src, Dst, Weight) ->
Out_update_fn = fun(Maybe_inner_map) -> case Maybe_inner_map of
{some, M} ->
gleam@dict:insert(M, Dst, Weight);
none ->
maps:from_list([{Dst, Weight}])
end end,
In_update_fn = fun(Maybe_inner_map@1) -> case Maybe_inner_map@1 of
{some, M@1} ->
gleam@dict:insert(M@1, Src, Weight);
none ->
maps:from_list([{Src, Weight}])
end end,
New_out = gleam@dict:upsert(erlang:element(4, Graph), Src, Out_update_fn),
New_in = gleam@dict:upsert(erlang:element(5, Graph), Dst, In_update_fn),
{graph, erlang:element(2, Graph), erlang:element(3, Graph), New_out, New_in}.
-file("src/yog/model.gleam", 73).
?DOC(
" Adds an edge to the graph with the given weight.\n"
"\n"
" For directed graphs, adds a single edge from `src` to `dst`.\n"
" For undirected graphs, adds edges in both directions.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" graph\n"
" |> model.add_edge(from: 1, to: 2, with: 10)\n"
" ```\n"
).
-spec add_edge(graph(DTT, DTU), integer(), integer(), DTU) -> graph(DTT, DTU).
add_edge(Graph, Src, Dst, Weight) ->
Graph@1 = do_add_directed_edge(Graph, Src, Dst, Weight),
case erlang:element(2, Graph@1) of
directed ->
Graph@1;
undirected ->
do_add_directed_edge(Graph@1, Dst, Src, Weight)
end.