#![forbid(unsafe_code)]
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Edge {
pub source: usize,
pub target: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DirectedEdge {
pub source: usize,
pub target: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UndirectedEdge {
pub a: usize,
pub b: usize,
}
impl Edge {
#[must_use]
pub const fn new(source: usize, target: usize) -> Self {
Self { source, target }
}
#[must_use]
pub const fn source(&self) -> usize {
self.source
}
#[must_use]
pub const fn target(&self) -> usize {
self.target
}
#[must_use]
pub const fn contains(&self, node: usize) -> bool {
self.source == node || self.target == node
}
#[must_use]
pub const fn reversed(&self) -> Self {
Self::new(self.target, self.source)
}
}
impl DirectedEdge {
#[must_use]
pub const fn new(source: usize, target: usize) -> Self {
Self { source, target }
}
#[must_use]
pub const fn source(&self) -> usize {
self.source
}
#[must_use]
pub const fn target(&self) -> usize {
self.target
}
#[must_use]
pub const fn contains(&self, node: usize) -> bool {
self.source == node || self.target == node
}
#[must_use]
pub const fn reversed(&self) -> Self {
Self::new(self.target, self.source)
}
}
impl UndirectedEdge {
#[must_use]
pub fn new(a: usize, b: usize) -> Self {
if a <= b {
Self { a, b }
} else {
Self { a: b, b: a }
}
}
#[must_use]
pub const fn source(&self) -> usize {
self.a
}
#[must_use]
pub const fn target(&self) -> usize {
self.b
}
#[must_use]
pub const fn contains(&self, node: usize) -> bool {
self.a == node || self.b == node
}
#[must_use]
pub fn reversed(&self) -> Self {
Self::new(self.b, self.a)
}
}
#[must_use]
pub fn dedupe_edges(edges: &[Edge]) -> Vec<Edge> {
let mut seen = HashSet::with_capacity(edges.len());
let mut unique = Vec::with_capacity(edges.len());
for &edge in edges {
if seen.insert(edge) {
unique.push(edge);
}
}
unique
}
#[must_use]
pub const fn has_self_loop(edge: Edge) -> bool {
edge.source == edge.target
}
#[cfg(test)]
mod tests {
use super::{dedupe_edges, has_self_loop, DirectedEdge, Edge, UndirectedEdge};
#[test]
fn constructs_and_reverses_edges() {
let edge = Edge::new(0, 1);
let directed = DirectedEdge::new(1, 2);
assert_eq!(edge.source(), 0);
assert_eq!(edge.target(), 1);
assert_eq!(edge.reversed(), Edge::new(1, 0));
assert!(directed.contains(2));
assert_eq!(directed.reversed(), DirectedEdge::new(2, 1));
}
#[test]
fn normalizes_undirected_edges() {
let first = UndirectedEdge::new(1, 2);
let second = UndirectedEdge::new(2, 1);
assert_eq!(first, second);
assert_eq!(first.source(), 1);
assert_eq!(first.target(), 2);
assert_eq!(first.reversed(), first);
}
#[test]
fn deduplicates_edges_and_detects_self_loops() {
let edges = [Edge::new(0, 1), Edge::new(0, 1), Edge::new(1, 1)];
assert_eq!(dedupe_edges(&edges), vec![Edge::new(0, 1), Edge::new(1, 1)]);
assert!(has_self_loop(Edge::new(1, 1)));
assert!(!has_self_loop(Edge::new(0, 1)));
}
}