1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

use std::hash::Hash;

use hashbrown::HashMap;
use petgraph::data::Create;
use petgraph::visit::{Data, EdgeCount, EdgeRef, IntoEdges, IntoNodeIdentifiers};

/// Constructs the line graph of an undirected graph.
///
/// The line graph `L(G)` of a graph `G` represents the adjacencies between edges of G.
/// `L(G)` contains a vertex for every edge in `G`, and `L(G)` contains an edge between two
/// vertices if the corresponding edges in `G` have a vertex in common.
///
/// Arguments:
///
/// * `input_graph` - The input graph `G`.
/// * `default_node_weight` - A callable that will return the weight to use
///     for newly created nodes.
/// * `default_edge_weight` - A callable that will return the weight object
///     to use for newly created edges.
///
/// Returns the constructed line graph `L(G)`, and the map from the edges of `L(G)` to
/// the vertices of `G`.
///
/// # Example
/// ```rust
/// use rustworkx_core::line_graph::line_graph;
/// use rustworkx_core::petgraph::visit::EdgeRef;
/// use rustworkx_core::petgraph::Graph;
/// use hashbrown::HashMap;
/// use petgraph::graph::{EdgeIndex, NodeIndex};
/// use petgraph::Undirected;
///
/// let input_graph =
///   Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2), (1, 2), (0, 3)]);
///
/// let (output_graph, output_edge_map): (
///     petgraph::graph::UnGraph<(), ()>,
///     HashMap<petgraph::prelude::EdgeIndex, petgraph::prelude::NodeIndex>,
/// ) = line_graph(&input_graph, || (), || ());
///
/// let output_edge_list = output_graph
///     .edge_references()
///     .map(|edge| (edge.source().index(), edge.target().index()))
///     .collect::<Vec<(usize, usize)>>();
///
/// let expected_edge_list = vec![(3, 1), (3, 0), (1, 0), (2, 0), (2, 1)];
/// let expected_edge_map: HashMap<EdgeIndex, NodeIndex> = [
///     (EdgeIndex::new(0), NodeIndex::new(0)),
///     (EdgeIndex::new(1), NodeIndex::new(1)),
///     (EdgeIndex::new(2), NodeIndex::new(2)),
///     (EdgeIndex::new(3), NodeIndex::new(3)),
/// ]
/// .into_iter()
/// .collect();
///
/// assert_eq!(output_edge_list, expected_edge_list);
/// assert_eq!(output_edge_map, expected_edge_map);
/// ```
pub fn line_graph<K, G, T, F, H, M>(
    input_graph: K,
    mut default_node_weight: F,
    mut default_edge_weight: H,
) -> (G, HashMap<K::EdgeId, G::NodeId>)
where
    K: EdgeCount + IntoNodeIdentifiers + IntoEdges,
    G: Create + Data<NodeWeight = T, EdgeWeight = M>,
    F: FnMut() -> T,
    H: FnMut() -> M,
    K::EdgeId: Hash + Eq,
{
    let num_edges = input_graph.edge_count();
    let mut output_graph = G::with_capacity(num_edges, 0);
    let mut output_edge_map =
        HashMap::<K::EdgeId, G::NodeId>::with_capacity(input_graph.edge_count());

    for edge in input_graph.edge_references() {
        let new_node = output_graph.add_node(default_node_weight());
        output_edge_map.insert(edge.id(), new_node);
    }

    for node in input_graph.node_identifiers() {
        let edges: Vec<K::EdgeRef> = input_graph.edges(node).collect();
        for i in 0..edges.len() {
            for j in i + 1..edges.len() {
                let node0 = output_edge_map.get(&edges[i].id()).unwrap();
                let node1 = output_edge_map.get(&edges[j].id()).unwrap();
                output_graph.add_edge(*node0, *node1, default_edge_weight());
            }
        }
    }
    (output_graph, output_edge_map)
}

#[cfg(test)]

mod test_line_graph {
    use crate::line_graph::line_graph;
    use crate::petgraph::visit::EdgeRef;
    use crate::petgraph::Graph;
    use hashbrown::HashMap;
    use petgraph::graph::{EdgeIndex, NodeIndex};
    use petgraph::Undirected;

    #[test]
    fn test_simple_graph() {
        // Simple graph
        let input_graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (2, 3), (3, 4), (4, 5)]);

        let (output_graph, output_edge_map): (
            petgraph::graph::UnGraph<(), ()>,
            HashMap<petgraph::prelude::EdgeIndex, petgraph::prelude::NodeIndex>,
        ) = line_graph(&input_graph, || (), || ());

        let output_edge_list = output_graph
            .edge_references()
            .map(|edge| (edge.source().index(), edge.target().index()))
            .collect::<Vec<(usize, usize)>>();

        let expected_edge_list = vec![(2, 1), (3, 2)];
        let expected_edge_map: HashMap<EdgeIndex, NodeIndex> = [
            (EdgeIndex::new(0), NodeIndex::new(0)),
            (EdgeIndex::new(1), NodeIndex::new(1)),
            (EdgeIndex::new(2), NodeIndex::new(2)),
            (EdgeIndex::new(3), NodeIndex::new(3)),
        ]
        .into_iter()
        .collect();

        assert_eq!(output_edge_list, expected_edge_list);
        assert_eq!(output_edge_map, expected_edge_map);
    }
}