Add GraphTopology.incidence_cursor() for certain use-cases where the iterator is problematic

Also add raw_incidences() and step_incidence() to AppendGraph analogously to
Graph, which allows to remove RawIncidenceIterator and simplify its call sites.
This commit is contained in:
2026-06-26 23:36:41 +02:00
parent c197766229
commit f009a86f1e
3 changed files with 54 additions and 27 deletions
+27 -25
View File
@@ -1,5 +1,5 @@
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::{EdgeMap, VertexMap};
use crate::traits::GraphTopology; use crate::traits::{GraphTopology, IncidenceCursor};
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Vertex(usize); pub struct Vertex(usize);
@@ -18,24 +18,20 @@ struct VertexIncidenceHeader {
first_incidence: Option<Incidence>, first_incidence: Option<Incidence>,
} }
#[derive(Copy, Clone)]
struct IncidenceEntry { struct IncidenceEntry {
next: Option<Incidence>, next: Option<Incidence>,
adjacent: Vertex, adjacent: Vertex,
} }
struct RawIncidenceIterator<'a> { #[derive(Copy, Clone)]
graph: &'a AppendGraph, pub struct AppendGraphIncidenceCursor {
incidence: Option<Incidence>, incidence: Option<Incidence>,
} }
impl<'a> Iterator for RawIncidenceIterator<'a> { impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor {
type Item = (Vertex, Incidence); fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Incidence)> {
graph.step_incidence(&mut self.incidence)
fn next(&mut self) -> Option<Self::Item> {
let current = self.incidence?;
let entry = &self.graph.incidences[current.0];
self.incidence = entry.next;
Some((entry.adjacent, current))
} }
} }
@@ -62,6 +58,18 @@ impl AppendGraph {
self.vertices[v1.0].incidence_count += 1; self.vertices[v1.0].incidence_count += 1;
self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1));
} }
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = (Vertex, Incidence)> {
let mut incidence = self.vertices[v.0].first_incidence;
std::iter::from_fn(move || self.step_incidence(&mut incidence))
}
fn step_incidence(&self, incidence: &mut Option<Incidence>) -> Option<(Vertex, Incidence)> {
let current = (*incidence)?;
let entry = self.incidences[current.0];
*incidence = entry.next;
Some((entry.adjacent, current))
}
} }
impl Default for AppendGraph { impl Default for AppendGraph {
@@ -72,8 +80,8 @@ impl Default for AppendGraph {
impl GraphTopology for AppendGraph { impl GraphTopology for AppendGraph {
type Vertex = Vertex; type Vertex = Vertex;
type Edge = Incidence; type Edge = Incidence;
type IncidenceCursor = AppendGraphIncidenceCursor;
fn vertex_count(&self) -> usize { fn vertex_count(&self) -> usize {
self.vertices.len() self.vertices.len()
@@ -112,11 +120,7 @@ impl GraphTopology for AppendGraph {
} }
fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex> { fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex> {
RawIncidenceIterator { self.raw_incidences(v).map(|(v, _)| v)
graph: self,
incidence: self.vertices[v.0].first_incidence,
}
.map(|(v, _)| v)
} }
fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex) { fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex) {
@@ -130,19 +134,17 @@ impl GraphTopology for AppendGraph {
} }
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> { fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> {
RawIncidenceIterator { self.raw_incidences(v).map(|(_, e)| e.normalize())
graph: self,
incidence: self.vertices[v.0].first_incidence,
}
.map(|(_, e)| e.normalize())
} }
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)> { fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)> {
RawIncidenceIterator { self.raw_incidences(v).map(|(v, e)| (v, e.normalize()))
graph: self, }
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor {
AppendGraphIncidenceCursor {
incidence: self.vertices[v.0].first_incidence, incidence: self.vertices[v.0].first_incidence,
} }
.map(|(v, e)| (v, e.normalize()))
} }
fn add_vertex(&mut self) -> Self::Vertex { fn add_vertex(&mut self) -> Self::Vertex {
+21 -2
View File
@@ -1,7 +1,7 @@
use typed_generational_arena::{Arena, Index}; use typed_generational_arena::{Arena, Index};
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::{EdgeMap, VertexMap};
use crate::traits::{GraphTopology, GraphTopologyDeletion}; use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
type Vertex = Index<VertexIncidenceHeader, usize, usize>; type Vertex = Index<VertexIncidenceHeader, usize, usize>;
@@ -34,6 +34,19 @@ impl IncidentEdgeCursor {
} }
} }
#[derive(Copy, Clone)]
pub struct GraphIncidenceCursor {
incidence: Option<IncidenceSlot>,
}
impl IncidenceCursor<Graph> for GraphIncidenceCursor {
fn next(&mut self, graph: &Graph) -> Option<(Vertex, Edge)> {
graph
.step_incidence(&mut self.incidence)
.map(|(vs, e)| (graph.vertices.get_idx(vs.0).unwrap(), e))
}
}
pub struct Graph { pub struct Graph {
// TODO: Arena index and generation types could be externalized to Graph. // TODO: Arena index and generation types could be externalized to Graph.
vertices: Arena<VertexIncidenceHeader, usize, usize>, vertices: Arena<VertexIncidenceHeader, usize, usize>,
@@ -137,8 +150,8 @@ impl Default for Graph {
impl GraphTopology for Graph { impl GraphTopology for Graph {
type Vertex = Vertex; type Vertex = Vertex;
type Edge = Edge; type Edge = Edge;
type IncidenceCursor = GraphIncidenceCursor;
fn vertex_count(&self) -> usize { fn vertex_count(&self) -> usize {
self.vertices.len() self.vertices.len()
@@ -210,6 +223,12 @@ impl GraphTopology for Graph {
.map(|(vs, e)| (self.vertices.get_idx(vs.0).unwrap(), self.normalize_edge(e))) .map(|(vs, e)| (self.vertices.get_idx(vs.0).unwrap(), self.normalize_edge(e)))
} }
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor {
GraphIncidenceCursor {
incidence: self.vertices[v].first_incidence,
}
}
fn add_vertex(&mut self) -> Self::Vertex { fn add_vertex(&mut self) -> Self::Vertex {
self.vertices.insert(VertexIncidenceHeader { self.vertices.insert(VertexIncidenceHeader {
incidence_count: 0, incidence_count: 0,
+6
View File
@@ -5,6 +5,7 @@ use crate::maps::{EdgeMap, VertexMap};
pub trait GraphTopology { pub trait GraphTopology {
type Vertex: Copy + Eq; type Vertex: Copy + Eq;
type Edge: Copy + Eq; type Edge: Copy + Eq;
type IncidenceCursor: IncidenceCursor<Self> + Copy;
fn vertex_count(&self) -> usize; fn vertex_count(&self) -> usize;
fn vertex_capacity(&self) -> usize; fn vertex_capacity(&self) -> usize;
@@ -20,6 +21,7 @@ pub trait GraphTopology {
fn edges(&self) -> impl Iterator<Item = Self::Edge>; fn edges(&self) -> impl Iterator<Item = Self::Edge>;
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>; fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>;
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>; fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>;
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor;
fn add_vertex(&mut self) -> Self::Vertex; fn add_vertex(&mut self) -> Self::Vertex;
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge; fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge;
} }
@@ -28,3 +30,7 @@ pub trait GraphTopologyDeletion: GraphTopology {
fn delete_vertex(&mut self, v: Self::Vertex); fn delete_vertex(&mut self, v: Self::Vertex);
fn delete_edge(&mut self, e: Self::Edge); fn delete_edge(&mut self, e: Self::Edge);
} }
pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>;
}