defmodule BitGraph.Algorithm.SCC.Kozaraju do alias BitGraph.{Dfs, Algorithm} import BitGraph.Algorithm.SCC.Utils import BitGraph.Algorithm @behaviour Algorithm ## Kozaraju's SCC algorithm @impl true def run( graph, opts \\ [] ) do (BitGraph.num_vertices(graph) == 0 && []) || ( component_handler = opts |> Keyword.get(:component_handler, {fn component, acc -> [component | acc] end, []}) |> wrap_component_handler() graph |> dfs(opts) |> Dfs.order(:out, :desc) |> Enum.reduce({nil, nil}, fn v, {state_acc, components_acc} -> state = dfs(graph, vertices: v, direction: :reverse, state: state_acc, process_vertex_fun: fn %{acc: acc} = _state, vertex -> MapSet.put(acc || MapSet.new(), vertex) end ) component = state[:acc] {Map.put(state, :acc, nil), (component && component_handler.(component, components_acc)) || components_acc} end) |> elem(1) ) end @impl true def preprocess(graph, opts) do Dfs.preprocess(graph, opts) end @impl true def postprocess(_graph, res) do res end def strongly_connected?(graph, opts \\ []) do try do component_handler = fn component, _acc -> throw({:single_scc?, component && MapSet.size(component) == BitGraph.num_vertices(graph)}) end run( graph, Keyword.merge(opts, component_handler: component_handler, process_vertex_fun: fn state, vertex, event -> (event == :processed && state.dag && throw({:error, :dag, vertex})) || state[:acc] end ) ) catch {:single_scc?, res} -> res {:error, :dag, _vertex} -> false end end end