<commit_before>-module(fc_find_path).

-export([path_flow_rules/3]).

path_flow_rules(Ep1, Ep2, Graph) ->
    VerticesEdges = vertices_edges_path(Graph, Ep1, Ep2),
    FlowRules = flow_rules(VerticesEdges, Graph),
    [{DpId, lists:append([TheFlowRules || {TheDpId, TheFlowRules} <- FlowRules,
					  TheDpId =:= DpId])}
     || DpId <- lists:usort(lists:map(fun({DpId, _}) -> DpId end, FlowRules))].

vertices_edges_path(Graph, Ep1, Ep2) ->
    %% TODO: check type of edges, instead of blindly using digraph functions.
    %% This will become a problem once we have the higher layers in the digraph.
    Path = digraph:get_path(Graph, Ep1, Ep2),
    vertices_edges(Path, Graph).

vertices_edges([Id], Graph) ->
    {Id, #{type := Type} = Metadata} = digraph:vertex(Graph, Id),
    [{Id, Type, Metadata}];
vertices_edges([Id1, Id2 | _] = [Id1 | Tail], Graph) ->
    {Id1, #{type := Type1} = Metadata1} = digraph:vertex(Graph, Id1),
    {Id2, #{}} = digraph:vertex(Graph, Id2),
    %% TODO: is there a nicer way to get the edge between Id1 and Id2?
    [Edge] = edges_between(Id1, Id2, Graph),
    {Edge, Id1, Id2, #{type := EdgeType}} = digraph:edge(Graph, Edge),
    [{Id1, Type1, Metadata1}, EdgeType]
	++ vertices_edges(Tail, Graph).

flow_rules([{_, endpoint, _}], _Graph) ->
    %% Nothing more to do.
    [];
flow_rules([{_, endpoint, _}, connected_to | [{_, of_port, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{_, of_port, _}, connected_to | [{_, of_port, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{_, of_port, _}, connected_to | [{_, endpoint, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{Port1, of_port, _}, port_of, {_Switch, of_switch, SwitchMetadata},
	    port_of | [{Port2, of_port, _} | _] = Tail], Graph) ->
    %% Add bidirectional flow rules.
    SwitchId = maps:get(datapath_id, SwitchMetadata),
    Rule1 = {
      [{in_port, fc_utils:id_to_port_no(Port1)}],
      [{apply_actions, [{output, fc_utils:id_to_port_no(Port2), no_buffer}]}],
      []},
    Rule2 = {
      [{in_port, fc_utils:id_to_port_no(Port2)}],
      [{apply_actions, [{output, fc_utils:id_to_port_no(Port1), no_buffer}]}],
      []},
    [{SwitchId, [Rule1, Rule2]}]
	++ flow_rules(Tail, Graph).

edges_between(Id1, Id2, Graph) ->
    OutEdges1 = digraph:out_edges(Graph, Id1),
    InEdges2 = digraph:in_edges(Graph, Id2),
    [Edge || Edge <- OutEdges1, lists:member(Edge, InEdges2)].
<commit_msg>Signal error if there is no path<commit_after>-module(fc_find_path).

-export([path_flow_rules/3]).

path_flow_rules(Ep1, Ep2, Graph) ->
    VerticesEdges = vertices_edges_path(Graph, Ep1, Ep2),
    FlowRules = flow_rules(VerticesEdges, Graph),
    [{DpId, lists:append([TheFlowRules || {TheDpId, TheFlowRules} <- FlowRules,
					  TheDpId =:= DpId])}
     || DpId <- lists:usort(lists:map(fun({DpId, _}) -> DpId end, FlowRules))].

vertices_edges_path(Graph, Ep1, Ep2) ->
    %% TODO: check type of edges, instead of blindly using digraph functions.
    %% This will become a problem once we have the higher layers in the digraph.
    Path = digraph:get_path(Graph, Ep1, Ep2),
    Path =/= false orelse error(no_path_found, [Graph, Ep1, Ep2]),
    vertices_edges(Path, Graph).

vertices_edges([Id], Graph) ->
    {Id, #{type := Type} = Metadata} = digraph:vertex(Graph, Id),
    [{Id, Type, Metadata}];
vertices_edges([Id1, Id2 | _] = [Id1 | Tail], Graph) ->
    {Id1, #{type := Type1} = Metadata1} = digraph:vertex(Graph, Id1),
    {Id2, #{}} = digraph:vertex(Graph, Id2),
    %% TODO: is there a nicer way to get the edge between Id1 and Id2?
    [Edge] = edges_between(Id1, Id2, Graph),
    {Edge, Id1, Id2, #{type := EdgeType}} = digraph:edge(Graph, Edge),
    [{Id1, Type1, Metadata1}, EdgeType]
	++ vertices_edges(Tail, Graph).

flow_rules([{_, endpoint, _}], _Graph) ->
    %% Nothing more to do.
    [];
flow_rules([{_, endpoint, _}, connected_to | [{_, of_port, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{_, of_port, _}, connected_to | [{_, of_port, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{_, of_port, _}, connected_to | [{_, endpoint, _} | _] = Tail], Graph) ->
    flow_rules(Tail, Graph);
flow_rules([{Port1, of_port, _}, port_of, {_Switch, of_switch, SwitchMetadata},
	    port_of | [{Port2, of_port, _} | _] = Tail], Graph) ->
    %% Add bidirectional flow rules.
    SwitchId = maps:get(datapath_id, SwitchMetadata),
    Rule1 = {
      [{in_port, fc_utils:id_to_port_no(Port1)}],
      [{apply_actions, [{output, fc_utils:id_to_port_no(Port2), no_buffer}]}],
      []},
    Rule2 = {
      [{in_port, fc_utils:id_to_port_no(Port2)}],
      [{apply_actions, [{output, fc_utils:id_to_port_no(Port1), no_buffer}]}],
      []},
    [{SwitchId, [Rule1, Rule2]}]
	++ flow_rules(Tail, Graph).

edges_between(Id1, Id2, Graph) ->
    OutEdges1 = digraph:out_edges(Graph, Id1),
    InEdges2 = digraph:in_edges(Graph, Id2),
    [Edge || Edge <- OutEdges1, lists:member(Edge, InEdges2)].

{'fraction_non_alphanumeric': 0.17704590818363272, 'fraction_numerical': 0.018363273453093812, 'mean_word_length': 4.123721881390593, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1417815', 'n_tokens_mistral': 1948, 'n_tokens_neox': 1911, 'n_words': 405}