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.
|
/// Convenience re-exports of graph topology traits for common use.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::traits::{
|
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 std::ops::IndexMut;
|
||||||
|
|
||||||
use crate::maps::ElementMap;
|
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`].
|
/// An opaque handle identifying a vertex in a [`FrozenGraph`].
|
||||||
///
|
///
|
||||||
@@ -103,67 +103,6 @@ pub struct FrozenGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = usize> {
|
||||||
self.vertices[v.0]..self.vertices[v.0 + 1]
|
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 {
|
impl GraphTopology for FrozenGraph {
|
||||||
type Vertex = Vertex;
|
type Vertex = Vertex;
|
||||||
type Edge = Edge;
|
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)]
|
#[cfg(test)]
|
||||||
mod trait_tests {
|
mod trait_tests {
|
||||||
use super::FrozenGraph;
|
use super::FrozenGraph;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::fmt::Debug;
|
|||||||
|
|
||||||
use crate::maps::ElementMap;
|
use crate::maps::ElementMap;
|
||||||
use crate::models::{AppendGraph, FrozenGraph};
|
use crate::models::{AppendGraph, FrozenGraph};
|
||||||
use crate::traits::{GraphTopology, GraphTopologyAddition, Incidence};
|
use crate::traits::{FromGraph, GraphTopology, GraphTopologyAddition, Incidence};
|
||||||
|
|
||||||
pub trait MakeTestGraph: GraphTopology + Sized {
|
pub trait MakeTestGraph: GraphTopology + Sized {
|
||||||
fn standard() -> (
|
fn standard() -> (
|
||||||
|
|||||||
@@ -237,6 +237,39 @@ pub trait GraphTopologyDeletion: GraphTopology {
|
|||||||
fn delete_edge(&mut self, e: Self::Edge);
|
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.
|
/// 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
|
/// Because the cursor is [`Copy`], its state can be saved and restored to replay or branch a
|
||||||
|
|||||||
Reference in New Issue
Block a user