diff --git a/src/traits.rs b/src/traits.rs index e3112bb..7c948c2 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,35 +2,244 @@ use crate::maps::{EdgeMap, VertexMap}; // TODO: Add functions to reserve memory for vertices and edges. // TODO: Split out GraphTopologyAddition trait. +/// A trait representing an undirected graph topology. +/// +/// An undirected graph is a set of vertices and undirected edges, where each edge connects either +/// exactly two vertices or one vertex with itself (loop edge). This trait provides methods for +/// querying a graph topology, iterating over vertices and edges, and adding new vertices and edges. +/// +/// # Vertices and Edges +/// +/// Vertices and edges are identified by opaque handles ([`Vertex`] and [`Edge`]) that implement +/// [`Copy`] and [`Eq`]. Handles remain valid for the lifetime of the graph unless the graph also +/// implements [`GraphTopologyDeletion`] and the item identified by the handle is explicitly +/// deleted. +/// +/// Methods accepting vertices or edges as parameters panic if the handle was invalidated by a +/// deletion, and return incorrect results if the handle was not produced by this graph instance. +/// +/// # Deletion +/// +/// This trait covers graph construction and querying only. To delete vertices and edges, see +/// [`GraphTopologyDeletion`]. +/// +/// [`Edge`]: GraphTopology::Edge +/// [`Vertex`]: GraphTopology::Vertex pub trait GraphTopology { + /// An opaque, stable handle identifying a vertex. type Vertex: Copy + Eq; + + /// An opaque, stable handle identifying an edge. type Edge: Copy + Eq; + + /// A resumable position in the incidence list of a vertex. + /// + /// Prefer [`incidences`](Self::incidences) for straightforward iteration. A cursor is useful + /// when an algorithm needs to pause traversal, perform other graph queries or mutations, and + /// then continue from where it left off. This cursor type can be obtained via + /// [`incidence_cursor`](Self::incidence_cursor). type IncidenceCursor: IncidenceCursor + Copy; + /// Returns the number of vertices in the graph. fn vertex_count(&self) -> usize; + + /// Returns the total number of vertices the graph can hold without reallocating. fn vertex_capacity(&self) -> usize; + + /// Creates and returns a [`VertexMap`] with every slot initialised to `default`. + /// + /// # Examples + /// + /// ``` + /// # use grapherity::prelude::*; + /// # use grapherity::models::Graph; + /// let mut graph = Graph::new(); + /// let v1 = graph.add_vertex(); + /// let mut labels = graph.vertex_map("z"); + /// assert_eq!(labels[v1], "z"); + /// labels[v1] = "a"; + /// assert_eq!(labels[v1], "a"); + /// + /// // A new vertex is immediately available for read and write. + /// let v2 = graph.add_vertex(); + /// assert_eq!(labels[v2], "z"); + /// labels[v2] = "b"; + /// assert_eq!(labels[v2], "b"); + /// ``` fn vertex_map(&self, default: T) -> VertexMap; + + /// Returns the number of edges in the graph. fn edge_count(&self) -> usize; + + /// Returns the total number of edges the graph can hold without reallocating. fn edge_capacity(&self) -> usize; + + /// Creates and returns an [`EdgeMap`] with every slot initialised to `default`. + /// + /// # Examples + /// + /// ``` + /// # use grapherity::prelude::*; + /// # use grapherity::models::Graph; + /// let mut graph = Graph::new(); + /// let v1 = graph.add_vertex(); + /// let v2 = graph.add_vertex(); + /// let e1 = graph.add_edge(v1, v2); + /// let mut weights = graph.edge_map(5); + /// assert_eq!(weights[e1], 5); + /// weights[e1] = 1; + /// assert_eq!(weights[e1], 1); + /// + /// // A new edge is immediately available for read and write. + /// let e2 = graph.add_edge(v1, v2); + /// assert_eq!(weights[e2], 5); + /// weights[e2] = 2; + /// assert_eq!(weights[e2], 2); + /// ``` fn edge_map(&self, default: T) -> EdgeMap; + + /// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2. + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn degree(&self, v: Self::Vertex) -> usize; + + /// Returns `true` if there is at least one edge between `v1` and `v2`, and `false` otherwise. + /// + /// A vertex is adjacent to itself if and only if it has a loop edge. + /// + /// # Panics + /// + /// Panics if `v1` or `v2` is not a valid vertex of this graph. fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool; + + /// Returns an iterator over all vertices in the graph. fn vertices(&self) -> impl Iterator; + + /// Returns an iterator over all vertices adjacent to `v`. + /// + /// Algorithms may prefer this over [`incidences`](Self::incidences) if the edges are not + /// required, since implementors may be able to provide an iterator faster than the trivial + /// mapping. The complexity of this method must not exceed that of + /// [`incidences`](Self::incidences). + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator; + + /// Returns the two endpoints of edge `e`, which are identical if and only if `e` is a loop + /// edge. + /// + /// # Panics + /// + /// Panics if `e` is not a valid edge of this graph. fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex); + + /// Returns an iterator over all edges in the graph. fn edges(&self) -> impl Iterator; + + /// Returns an iterator over all edges incident to `v`. + /// + /// Algorithms may prefer this over [`incidences`](Self::incidences) if the vertices are not + /// required, since implementors may be able to provide an iterator faster than the trivial + /// mapping. The complexity of this method must not exceed that of + /// [`incidences`](Self::incidences). + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn incident_edges(&self, v: Self::Vertex) -> impl Iterator; + + /// Returns an iterator over all incidences of `v`, i.e. vertex-edge pairs `(u, e)` such that + /// `u` is adjacent to `v` and `e` is an edge between them. + /// + /// Use [`incidence_cursor`](Self::incidence_cursor) instead for a traversal that needs to + /// suspend and resume across other state updates. + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn incidences(&self, v: Self::Vertex) -> impl Iterator; + + /// Returns a cursor over all incidences of `v`, analogously to + /// [`incidences`](Self::incidences), initially positioned before the first one. + /// + /// See [`IncidenceCursor`](Self::IncidenceCursor) for when to prefer a cursor over + /// [`incidences`](Self::incidences). + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor; + + /// Adds a new isolated vertex and returns its handle. fn add_vertex(&mut self) -> Self::Vertex; + + /// Adds a new edge between `v1` and `v2` and returns its handle. + /// + /// # Panics + /// + /// Panics if `v1` or `v2` is not a valid vertex of this graph. fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge; } +/// A trait that adds deletion operations to an undirected graph topology. +/// +/// This trait provides methods for deletion of vertices and edges in an undirected graph. These +/// operations will invalidate handles to all deleted vertices and edges. Methods accepting invalid +/// vertices or edges as parameters panic. pub trait GraphTopologyDeletion: GraphTopology { + /// Deletes the vertex `v` and all its incident edges from the graph. Note that this also + /// invalidates the handles of all edges incident to `v`. + /// + /// # Panics + /// + /// Panics if `v` is not a valid vertex of this graph. fn delete_vertex(&mut self, v: Self::Vertex); + + /// Deletes the edge `e` from the graph. This operation only invalidates `e` and no other vertex + /// or edge handles. + /// + /// # Panics + /// + /// Panics if `e` is not a valid edge of this graph. fn delete_edge(&mut self, e: Self::Edge); } +/// A cursor for traversing the incidences of a vertex one step at a time. +/// +/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See +/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over +/// [`GraphTopology::incidences`]. pub trait IncidenceCursor { + /// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the + /// traversal is exhausted. + /// + /// # Examples + /// + /// ``` + /// # use grapherity::prelude::*; + /// # use grapherity::models::Graph; + /// // Constructs a graph with two vertices connected to `v`. + /// let mut graph = Graph::new(); + /// let v = graph.add_vertex(); + /// for _ in 0..2 { + /// let u = graph.add_vertex(); + /// graph.add_edge(u, v); + /// } + /// + /// // Iterates over the incidences of `v` with a cursor. + /// let mut c1 = graph.incidence_cursor(v); + /// assert!(c1.next(&graph).is_some()); + /// let mut c2 = c1; + /// // Continues iteration with original cursor. + /// assert!(c1.next(&graph).is_some()); + /// assert!(c1.next(&graph).is_none()); + /// // Iterates over the last incidence again with the copied cursor. + /// assert!(c2.next(&graph).is_some()); + /// assert!(c2.next(&graph).is_none()); + /// ``` fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>; }