Add FromGraph trait, and add implementation for FrozenGraph
This commit is contained in:
+2
-1
@@ -145,7 +145,8 @@ pub mod traits;
|
||||
/// Convenience re-exports of graph topology traits for common use.
|
||||
pub mod prelude {
|
||||
pub use crate::traits::{
|
||||
GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence, IncidenceCursor,
|
||||
FromGraph, GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence,
|
||||
IncidenceCursor,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+63
-68
@@ -3,7 +3,7 @@
|
||||
use std::ops::IndexMut;
|
||||
|
||||
use crate::maps::ElementMap;
|
||||
use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
|
||||
use crate::traits::{FromGraph, GraphTopology, Incidence, IncidenceCursor};
|
||||
|
||||
/// An opaque handle identifying a vertex in a [`FrozenGraph`].
|
||||
///
|
||||
@@ -103,67 +103,6 @@ pub struct FrozenGraph {
|
||||
}
|
||||
|
||||
impl FrozenGraph {
|
||||
// TODO: Move this to a new FromGraph trait and adapt the MakeTestGraph impl to use helper functions like "fn fixture_empty<G: FromGraph>() -> G { G::from_graph(&AppendGraph::empty()).0 }".
|
||||
/// Creates a graph instance as a copy of another graph.
|
||||
///
|
||||
/// Returns the copied graph instance and two mappings of the `source` graph vertices and edges
|
||||
/// to the new ones. Note that the returned mappings cannot support additional growth, i.e. any
|
||||
/// vertex or edge added to the `source` graph after calling `from_graph` will map to an invalid
|
||||
/// vertex or edge in the new `FrozenGraph`.
|
||||
pub fn from_graph<G: GraphTopology>(
|
||||
source: &G,
|
||||
) -> (
|
||||
Self,
|
||||
ElementMap<G::Vertex, Vertex>,
|
||||
ElementMap<G::Edge, Edge>,
|
||||
) {
|
||||
// Builds vertex list with sentinel and vertex map.
|
||||
let mut vertex_map = source.vertex_map(Vertex(usize::MAX));
|
||||
let mut vertices = Vec::with_capacity(source.vertex_count() + 1);
|
||||
let mut offset = 0;
|
||||
for (i, v) in source.vertices().enumerate() {
|
||||
vertex_map[v] = Vertex(i);
|
||||
vertices.push(offset);
|
||||
offset += source.degree(v);
|
||||
}
|
||||
vertices.push(offset);
|
||||
|
||||
// Builds incidence list and edge map. Uses "usize::MAX" as "unset" here: an edge can never
|
||||
// be normalized to "usize::MAX", since it must be the smaller index of two incidences.
|
||||
const UNSET: usize = usize::MAX;
|
||||
let mut edge_map = source.edge_map(Edge(UNSET));
|
||||
let mut incidences = Vec::with_capacity(source.edge_count() * 2);
|
||||
for v in source.vertices() {
|
||||
for Incidence { vertex: u, edge: e } in source.incidences(v) {
|
||||
let em = edge_map.index_mut(e);
|
||||
if em.0 == UNSET {
|
||||
// Adds the first incidence for the edge, but we don't know the opposite, yet.
|
||||
*em = Edge(incidences.len());
|
||||
incidences.push(IncidenceEntry {
|
||||
adjacent: vertex_map[u],
|
||||
opposite: 0,
|
||||
});
|
||||
} else {
|
||||
// Adds the second incidence for the edge and fills the opposite for the first.
|
||||
incidences[em.0].opposite = incidences.len();
|
||||
incidences.push(IncidenceEntry {
|
||||
adjacent: vertex_map[u],
|
||||
opposite: em.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Self {
|
||||
vertices,
|
||||
incidences,
|
||||
},
|
||||
vertex_map,
|
||||
edge_map,
|
||||
)
|
||||
}
|
||||
|
||||
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = usize> {
|
||||
self.vertices[v.0]..self.vertices[v.0 + 1]
|
||||
}
|
||||
@@ -186,12 +125,6 @@ impl FrozenGraph {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: GraphTopology> From<&G> for FrozenGraph {
|
||||
fn from(graph: &G) -> Self {
|
||||
Self::from_graph(graph).0
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphTopology for FrozenGraph {
|
||||
type Vertex = Vertex;
|
||||
type Edge = Edge;
|
||||
@@ -257,6 +190,68 @@ impl GraphTopology for FrozenGraph {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: GraphTopology> From<&G> for FrozenGraph {
|
||||
fn from(graph: &G) -> Self {
|
||||
Self::from_graph(graph).0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGraph for FrozenGraph {
|
||||
fn from_graph<G: GraphTopology>(
|
||||
source: &G,
|
||||
) -> (
|
||||
Self,
|
||||
ElementMap<G::Vertex, Vertex>,
|
||||
ElementMap<G::Edge, Edge>,
|
||||
) {
|
||||
// Builds vertex list with sentinel and vertex map.
|
||||
let mut vertex_map = source.vertex_map(Vertex(usize::MAX));
|
||||
let mut vertices = Vec::with_capacity(source.vertex_count() + 1);
|
||||
let mut offset = 0;
|
||||
for (i, v) in source.vertices().enumerate() {
|
||||
vertex_map[v] = Vertex(i);
|
||||
vertices.push(offset);
|
||||
offset += source.degree(v);
|
||||
}
|
||||
vertices.push(offset);
|
||||
|
||||
// Builds incidence list and edge map. Uses "usize::MAX" as "unset" here: an edge can never
|
||||
// be normalized to "usize::MAX", since it must be the smaller index of two incidences.
|
||||
const UNSET: usize = usize::MAX;
|
||||
let mut edge_map = source.edge_map(Edge(UNSET));
|
||||
let mut incidences = Vec::with_capacity(source.edge_count() * 2);
|
||||
for v in source.vertices() {
|
||||
for Incidence { vertex: u, edge: e } in source.incidences(v) {
|
||||
let em = edge_map.index_mut(e);
|
||||
if em.0 == UNSET {
|
||||
// Adds the first incidence for the edge, but we don't know the opposite, yet.
|
||||
*em = Edge(incidences.len());
|
||||
incidences.push(IncidenceEntry {
|
||||
adjacent: vertex_map[u],
|
||||
opposite: 0,
|
||||
});
|
||||
} else {
|
||||
// Adds the second incidence for the edge and fills the opposite for the first.
|
||||
incidences[em.0].opposite = incidences.len();
|
||||
incidences.push(IncidenceEntry {
|
||||
adjacent: vertex_map[u],
|
||||
opposite: em.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Self {
|
||||
vertices,
|
||||
incidences,
|
||||
},
|
||||
vertex_map,
|
||||
edge_map,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod trait_tests {
|
||||
use super::FrozenGraph;
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt::Debug;
|
||||
|
||||
use crate::maps::ElementMap;
|
||||
use crate::models::{AppendGraph, FrozenGraph};
|
||||
use crate::traits::{GraphTopology, GraphTopologyAddition, Incidence};
|
||||
use crate::traits::{FromGraph, GraphTopology, GraphTopologyAddition, Incidence};
|
||||
|
||||
pub trait MakeTestGraph: GraphTopology + Sized {
|
||||
fn standard() -> (
|
||||
|
||||
@@ -237,6 +237,39 @@ pub trait GraphTopologyDeletion: GraphTopology {
|
||||
fn delete_edge(&mut self, e: Self::Edge);
|
||||
}
|
||||
|
||||
/// A trait for constructing an undirected graph topology by converting another graph
|
||||
/// implementation to this one.
|
||||
pub trait FromGraph: GraphTopology {
|
||||
/// Creates a new graph instance that is isomorphic to `source`.
|
||||
///
|
||||
/// Returns the copied graph instance and two mappings from the vertices and edges of `source`
|
||||
/// to the corresponding vertices and edges of the returned graph.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use grapherity::prelude::*;
|
||||
/// use grapherity::models::{AppendGraph, FrozenGraph};
|
||||
///
|
||||
/// let mut source = AppendGraph::new();
|
||||
/// let u = source.add_vertex();
|
||||
/// let v = source.add_vertex();
|
||||
/// let e = source.add_edge(u, v);
|
||||
/// let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&source);
|
||||
///
|
||||
/// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v]));
|
||||
/// ```
|
||||
fn from_graph<G: GraphTopology>(
|
||||
source: &G,
|
||||
) -> (
|
||||
Self,
|
||||
ElementMap<G::Vertex, Self::Vertex>,
|
||||
ElementMap<G::Edge, Self::Edge>,
|
||||
)
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// A cursor for traversing the incidences of a vertex one step at a time.
|
||||
///
|
||||
/// Because the cursor is [`Copy`], its state can be saved and restored to replay or branch a
|
||||
|
||||
Reference in New Issue
Block a user