From f009a86f1e66f6369af0a7bbe8f3b80ef36c2392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Fri, 26 Jun 2026 23:36:41 +0200 Subject: [PATCH] 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. --- src/models/append_graph.rs | 52 ++++++++++++++++++++------------------ src/models/graph.rs | 23 +++++++++++++++-- src/traits.rs | 6 +++++ 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs index d0abc41..65657f8 100644 --- a/src/models/append_graph.rs +++ b/src/models/append_graph.rs @@ -1,5 +1,5 @@ use crate::maps::{EdgeMap, VertexMap}; -use crate::traits::GraphTopology; +use crate::traits::{GraphTopology, IncidenceCursor}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Vertex(usize); @@ -18,24 +18,20 @@ struct VertexIncidenceHeader { first_incidence: Option, } +#[derive(Copy, Clone)] struct IncidenceEntry { next: Option, adjacent: Vertex, } -struct RawIncidenceIterator<'a> { - graph: &'a AppendGraph, +#[derive(Copy, Clone)] +pub struct AppendGraphIncidenceCursor { incidence: Option, } -impl<'a> Iterator for RawIncidenceIterator<'a> { - type Item = (Vertex, Incidence); - - fn next(&mut self) -> Option { - let current = self.incidence?; - let entry = &self.graph.incidences[current.0]; - self.incidence = entry.next; - Some((entry.adjacent, current)) +impl IncidenceCursor for AppendGraphIncidenceCursor { + fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Incidence)> { + graph.step_incidence(&mut self.incidence) } } @@ -62,6 +58,18 @@ impl AppendGraph { self.vertices[v1.0].incidence_count += 1; self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); } + + fn raw_incidences(&self, v: Vertex) -> impl Iterator { + 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) -> Option<(Vertex, Incidence)> { + let current = (*incidence)?; + let entry = self.incidences[current.0]; + *incidence = entry.next; + Some((entry.adjacent, current)) + } } impl Default for AppendGraph { @@ -72,8 +80,8 @@ impl Default for AppendGraph { impl GraphTopology for AppendGraph { type Vertex = Vertex; - type Edge = Incidence; + type IncidenceCursor = AppendGraphIncidenceCursor; fn vertex_count(&self) -> usize { self.vertices.len() @@ -112,11 +120,7 @@ impl GraphTopology for AppendGraph { } fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator { - RawIncidenceIterator { - graph: self, - incidence: self.vertices[v.0].first_incidence, - } - .map(|(v, _)| v) + self.raw_incidences(v).map(|(v, _)| v) } 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 { - RawIncidenceIterator { - graph: self, - incidence: self.vertices[v.0].first_incidence, - } - .map(|(_, e)| e.normalize()) + self.raw_incidences(v).map(|(_, e)| e.normalize()) } fn incidences(&self, v: Self::Vertex) -> impl Iterator { - RawIncidenceIterator { - graph: self, + self.raw_incidences(v).map(|(v, e)| (v, e.normalize())) + } + + fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { + AppendGraphIncidenceCursor { incidence: self.vertices[v.0].first_incidence, } - .map(|(v, e)| (v, e.normalize())) } fn add_vertex(&mut self) -> Self::Vertex { diff --git a/src/models/graph.rs b/src/models/graph.rs index 025d388..309d268 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -1,7 +1,7 @@ use typed_generational_arena::{Arena, Index}; use crate::maps::{EdgeMap, VertexMap}; -use crate::traits::{GraphTopology, GraphTopologyDeletion}; +use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; type Vertex = Index; @@ -34,6 +34,19 @@ impl IncidentEdgeCursor { } } +#[derive(Copy, Clone)] +pub struct GraphIncidenceCursor { + incidence: Option, +} + +impl IncidenceCursor 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 { // TODO: Arena index and generation types could be externalized to Graph. vertices: Arena, @@ -137,8 +150,8 @@ impl Default for Graph { impl GraphTopology for Graph { type Vertex = Vertex; - type Edge = Edge; + type IncidenceCursor = GraphIncidenceCursor; fn vertex_count(&self) -> usize { 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))) } + fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { + GraphIncidenceCursor { + incidence: self.vertices[v].first_incidence, + } + } + fn add_vertex(&mut self) -> Self::Vertex { self.vertices.insert(VertexIncidenceHeader { incidence_count: 0, diff --git a/src/traits.rs b/src/traits.rs index fecd07c..e3112bb 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,6 +5,7 @@ use crate::maps::{EdgeMap, VertexMap}; pub trait GraphTopology { type Vertex: Copy + Eq; type Edge: Copy + Eq; + type IncidenceCursor: IncidenceCursor + Copy; fn vertex_count(&self) -> usize; fn vertex_capacity(&self) -> usize; @@ -20,6 +21,7 @@ pub trait GraphTopology { fn edges(&self) -> impl Iterator; fn incident_edges(&self, v: Self::Vertex) -> impl Iterator; fn incidences(&self, v: Self::Vertex) -> impl Iterator; + fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor; fn add_vertex(&mut self) -> Self::Vertex; 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_edge(&mut self, e: Self::Edge); } + +pub trait IncidenceCursor { + fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>; +}