-module(yog@generators@random). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/yog/generators/random.gleam"). -export([erdos_renyi_gnp_with_type/3, erdos_renyi_gnp/2, erdos_renyi_gnm_with_type/3, erdos_renyi_gnm/2, watts_strogatz_with_type/4, watts_strogatz/3, barabasi_albert_with_type/3, barabasi_albert/2, random_tree_with_type/2, random_tree/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Stochastic graph generators for random graph models.\n"). -file("src/yog/generators/random.gleam", 102). -spec add_random_edges( yog@model:graph(nil, integer()), integer(), integer(), gleam@set:set({integer(), integer()}), yog@model:graph_type() ) -> yog@model:graph(nil, integer()). add_random_edges(Graph, N, M, Existing, Graph_type) -> case M =< 0 of true -> Graph; false -> I = gleam@int:random(N), J = gleam@int:random(N), case I =:= J of true -> add_random_edges(Graph, N, M, Existing, Graph_type); false -> Edge = case Graph_type of undirected -> case I < J of true -> {I, J}; false -> {J, I} end; directed -> {I, J} end, case gleam@set:contains(Existing, Edge) of true -> add_random_edges(Graph, N, M, Existing, Graph_type); false -> New_graph = yog@model:add_edge( Graph, erlang:element(1, Edge), erlang:element(2, Edge), 1 ), New_existing = gleam@set:insert(Existing, Edge), add_random_edges( New_graph, N, M - 1, New_existing, Graph_type ) end end end. -file("src/yog/generators/random.gleam", 232). -spec build_degree_list(yog@model:graph(nil, integer()), yog@model:graph_type()) -> list(integer()). build_degree_list(Graph, Graph_type) -> _pipe = yog@model:all_nodes(Graph), gleam@list:flat_map( _pipe, fun(Node) -> Degree = case Graph_type of undirected -> erlang:length(yog@model:neighbors(Graph, Node)); directed -> erlang:length(yog@model:successors(Graph, Node)) end, gleam@list:repeat(Node, gleam@int:max(Degree, 1)) end ). -file("src/yog/generators/random.gleam", 320). -spec add_random_edge_not_to( yog@model:graph(nil, integer()), integer(), integer() ) -> yog@model:graph(nil, integer()). add_random_edge_not_to(Graph, From, N) -> To = gleam@int:random(N), case To =:= From of true -> add_random_edge_not_to(Graph, From, N); false -> Neighbors = yog@model:successors(Graph, From), Neighbor_ids = gleam@list:map( Neighbors, fun(Pair) -> erlang:element(1, Pair) end ), case gleam@list:contains(Neighbor_ids, To) of true -> add_random_edge_not_to(Graph, From, N); false -> yog@model:add_edge(Graph, From, To, 1) end end. -file("src/yog/generators/random.gleam", 399). -spec create_nodes(yog@model:graph(nil, RUT), integer()) -> yog@model:graph(nil, RUT). create_nodes(Graph, N) -> _pipe = yog@internal@utils:range(0, N - 1), gleam@list:fold( _pipe, Graph, fun(G, I) -> yog@model:add_node(G, I, nil) end ). -file("src/yog/generators/random.gleam", 26). ?DOC(" Generates an Erdős-Rényi G(n, p) graph with specified graph type.\n"). -spec erdos_renyi_gnp_with_type(integer(), float(), yog@model:graph_type()) -> yog@model:graph(nil, integer()). erdos_renyi_gnp_with_type(N, P, Graph_type) -> Graph = create_nodes(yog@model:new(Graph_type), N), case Graph_type of undirected -> _pipe = yog@internal@utils:range(0, N - 1), gleam@list:fold( _pipe, Graph, fun(G, I) -> _pipe@1 = yog@internal@utils:range(I + 1, N - 1), gleam@list:fold( _pipe@1, G, fun(Acc, J) -> case rand:uniform() < P of true -> yog@model:add_edge(Acc, I, J, 1); false -> Acc end end ) end ); directed -> _pipe@2 = yog@internal@utils:range(0, N - 1), gleam@list:fold( _pipe@2, Graph, fun(G@1, I@1) -> _pipe@3 = yog@internal@utils:range(0, N - 1), gleam@list:fold( _pipe@3, G@1, fun(Acc@1, J@1) -> case I@1 =:= J@1 of true -> Acc@1; false -> case rand:uniform() < P of true -> yog@model:add_edge( Acc@1, I@1, J@1, 1 ); false -> Acc@1 end end end ) end ) end. -file("src/yog/generators/random.gleam", 21). ?DOC( " Generates a random graph using the Erdős-Rényi G(n, p) model.\n" "\n" " Each possible edge is included independently with probability p.\n" "\n" " **Time Complexity:** O(n²)\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let graph = random.erdos_renyi_gnp(50, 0.1)\n" " ```\n" ). -spec erdos_renyi_gnp(integer(), float()) -> yog@model:graph(nil, integer()). erdos_renyi_gnp(N, P) -> erdos_renyi_gnp_with_type(N, P, undirected). -file("src/yog/generators/random.gleam", 83). ?DOC(" Generates an Erdős-Rényi G(n, m) graph with specified graph type.\n"). -spec erdos_renyi_gnm_with_type(integer(), integer(), yog@model:graph_type()) -> yog@model:graph(nil, integer()). erdos_renyi_gnm_with_type(N, M, Graph_type) -> Graph = create_nodes(yog@model:new(Graph_type), N), Max_edges = case Graph_type of undirected -> (N * (N - 1)) div 2; directed -> N * (N - 1) end, Actual_m = gleam@int:min(M, Max_edges), add_random_edges(Graph, N, Actual_m, gleam@set:new(), Graph_type). -file("src/yog/generators/random.gleam", 78). ?DOC( " Generates a random graph using the Erdős-Rényi G(n, m) model.\n" "\n" " Exactly m edges are added uniformly at random.\n" "\n" " **Time Complexity:** O(m) expected\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let graph = random.erdos_renyi_gnm(50, 100)\n" " ```\n" ). -spec erdos_renyi_gnm(integer(), integer()) -> yog@model:graph(nil, integer()). erdos_renyi_gnm(N, M) -> erdos_renyi_gnm_with_type(N, M, undirected). -file("src/yog/generators/random.gleam", 284). ?DOC(" Generates a Watts-Strogatz graph with specified graph type.\n"). -spec watts_strogatz_with_type( integer(), integer(), float(), yog@model:graph_type() ) -> yog@model:graph(nil, integer()). watts_strogatz_with_type(N, K, P, Graph_type) -> case ((N < 3) orelse (K < 2)) orelse (K >= N) of true -> yog@model:new(Graph_type); false -> Graph = create_nodes(yog@model:new(Graph_type), N), Half_k = K div 2, _pipe = yog@internal@utils:range(0, N - 1), gleam@list:fold( _pipe, Graph, fun(G, I) -> _pipe@1 = yog@internal@utils:range(1, Half_k), gleam@list:fold( _pipe@1, G, fun(Acc, Offset) -> case rand:uniform() < P of false -> J = case N of 0 -> 0; Gleam@denominator -> (I + Offset) rem Gleam@denominator end, yog@model:add_edge(Acc, I, J, 1); true -> add_random_edge_not_to(Acc, I, N) end end ) end ) end. -file("src/yog/generators/random.gleam", 279). ?DOC( " Generates a small-world network using the Watts-Strogatz model.\n" "\n" " Creates a ring lattice where each node connects to k nearest neighbors,\n" " then rewires with probability p.\n" "\n" " **Time Complexity:** O(nk)\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let graph = random.watts_strogatz(100, 4, 0.1)\n" " ```\n" ). -spec watts_strogatz(integer(), integer(), float()) -> yog@model:graph(nil, integer()). watts_strogatz(N, K, P) -> watts_strogatz_with_type(N, K, P, undirected). -file("src/yog/generators/random.gleam", 405). -spec list_at(list(RUY), integer()) -> {ok, RUY} | {error, nil}. list_at(Lst, Index) -> case {Index, Lst} of {0, [First | _]} -> {ok, First}; {N, [_ | Rest]} when N > 0 -> list_at(Rest, N - 1); {_, _} -> {error, nil} end. -file("src/yog/generators/random.gleam", 245). -spec select_preferential_targets( list(integer()), integer(), gleam@set:set(integer()) ) -> gleam@set:set(integer()). select_preferential_targets(Degree_list, M, Selected) -> case (gleam@set:size(Selected) >= M) orelse gleam@list:is_empty(Degree_list) of true -> Selected; false -> List_size = erlang:length(Degree_list), Index = gleam@int:random(List_size), case list_at(Degree_list, Index) of {ok, Target} -> New_selected = gleam@set:insert(Selected, Target), select_preferential_targets(Degree_list, M, New_selected); {error, _} -> Selected end end. -file("src/yog/generators/random.gleam", 209). -spec add_node_with_preferential_attachment( yog@model:graph(nil, integer()), integer(), integer(), yog@model:graph_type() ) -> yog@model:graph(nil, integer()). add_node_with_preferential_attachment(Graph, New_node, M, Graph_type) -> With_node = yog@model:add_node(Graph, New_node, nil), Degree_list = build_degree_list(Graph, Graph_type), Targets = select_preferential_targets(Degree_list, M, gleam@set:new()), _pipe = Targets, _pipe@1 = gleam@set:to_list(_pipe), gleam@list:fold( _pipe@1, With_node, fun(G, Target) -> yog@model:add_edge(G, New_node, Target, 1) end ). -file("src/yog/generators/random.gleam", 160). ?DOC(" Generates a Barabási-Albert graph with specified graph type.\n"). -spec barabasi_albert_with_type(integer(), integer(), yog@model:graph_type()) -> yog@model:graph(nil, integer()). barabasi_albert_with_type(N, M, Graph_type) -> case (N < M) orelse (M < 1) of true -> yog@model:new(Graph_type); false -> M0 = gleam@int:max(M, 2), Initial = begin _pipe = yog@internal@utils:range(0, M0 - 1), gleam@list:fold( _pipe, yog@model:new(Graph_type), fun(G, I) -> yog@model:add_node(G, I, nil) end ) end, Initial_with_edges = case Graph_type of undirected -> _pipe@1 = yog@internal@utils:range(0, M0 - 1), gleam@list:fold( _pipe@1, Initial, fun(G@1, I@1) -> _pipe@2 = yog@internal@utils:range(I@1 + 1, M0 - 1), gleam@list:fold( _pipe@2, G@1, fun(Acc, J) -> yog@model:add_edge(Acc, I@1, J, 1) end ) end ); directed -> _pipe@3 = yog@internal@utils:range(0, M0 - 1), gleam@list:fold( _pipe@3, Initial, fun(G@2, I@2) -> _pipe@4 = yog@internal@utils:range(0, M0 - 1), gleam@list:fold( _pipe@4, G@2, fun(Acc@1, J@1) -> case I@2 =:= J@1 of true -> Acc@1; false -> yog@model:add_edge( Acc@1, I@2, J@1, 1 ) end end ) end ) end, _pipe@5 = yog@internal@utils:range(M0, N - 1), gleam@list:fold( _pipe@5, Initial_with_edges, fun(G@3, New_node) -> add_node_with_preferential_attachment( G@3, New_node, M, Graph_type ) end ) end. -file("src/yog/generators/random.gleam", 155). ?DOC( " Generates a scale-free network using the Barabási-Albert model.\n" "\n" " Starts with m₀ nodes in a complete graph, then adds nodes using preferential attachment.\n" "\n" " **Time Complexity:** O(nm)\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let graph = random.barabasi_albert(100, 3)\n" " ```\n" ). -spec barabasi_albert(integer(), integer()) -> yog@model:graph(nil, integer()). barabasi_albert(N, M) -> barabasi_albert_with_type(N, M, undirected). -file("src/yog/generators/random.gleam", 371). -spec build_random_tree( yog@model:graph(nil, integer()), integer(), gleam@set:set(integer()), integer() ) -> yog@model:graph(nil, integer()). build_random_tree(Graph, N, In_tree, Next_node) -> case Next_node >= N of true -> Graph; false -> Tree_list = gleam@set:to_list(In_tree), Tree_size = erlang:length(Tree_list), Index = gleam@int:random(Tree_size), case list_at(Tree_list, Index) of {ok, Parent} -> New_graph = yog@model:add_edge(Graph, Parent, Next_node, 1), New_in_tree = gleam@set:insert(In_tree, Next_node), build_random_tree(New_graph, N, New_in_tree, Next_node + 1); {error, _} -> Graph end end. -file("src/yog/generators/random.gleam", 355). ?DOC(" Generates a random tree with specified graph type.\n"). -spec random_tree_with_type(integer(), yog@model:graph_type()) -> yog@model:graph(nil, integer()). random_tree_with_type(N, Graph_type) -> case N < 2 of true -> create_nodes(yog@model:new(Graph_type), N); false -> Graph = create_nodes(yog@model:new(Graph_type), N), In_tree = gleam@set:from_list([0]), build_random_tree(Graph, N, In_tree, 1) end. -file("src/yog/generators/random.gleam", 350). ?DOC( " Generates a uniformly random tree on n nodes.\n" "\n" " **Time Complexity:** O(n²) expected\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let tree = random.random_tree(50)\n" " ```\n" ). -spec random_tree(integer()) -> yog@model:graph(nil, integer()). random_tree(N) -> random_tree_with_type(N, undirected).