From 310abffd5b122758df28e6fc9ff79e106eabd000 Mon Sep 17 00:00:00 2001 From: Emad Jacob Maroun Date: Sun, 19 Jan 2025 17:16:38 +0100 Subject: [PATCH] Split 'HasVertex' into itself and 'VertexIn'. 'HasVertex' is now only about whether there is a vertex. 'VertexIn' specifies a list of vertices guaranteed to be in the graph. --- src/algo/bfs.rs | 20 +-- src/algo/dfs.rs | 8 +- src/algo/dijkstra_shortest_paths.rs | 12 +- src/algo/mod.rs | 1 - src/algo/tarjan_scc.rs | 13 +- src/core/property/connected.rs | 10 +- src/core/property/has_vertex.rs | 159 ++++++++++-------- src/core/property/impl_ensurer.rs | 38 ++++- src/core/property/rooted.rs | 4 +- src/core/property/unilateral.rs | 1 + src/core/proxy/reverse_graph.rs | 2 +- tests/algo/bfs.rs | 8 +- tests/algo/dfs.rs | 4 +- tests/algo/dijkstra_shortest_paths.rs | 8 +- tests/algo/search.rs | 4 +- tests/common/adjacency_list/impl_graph.rs | 8 +- tests/common/ensured.rs | 4 +- tests/core/graph/edge_lookup_methods.rs | 4 +- tests/core/property/acyclic.rs | 4 +- tests/core/property/connectedness.rs | 8 +- tests/core/property/has_vertex_rooted.rs | 44 ++--- tests/core/property/unique.rs | 4 +- tests/mock_graph/arbitrary/acyclic_graph.rs | 6 +- .../arbitrary/combinations/edge_in.rs | 6 +- .../arbitrary/combinations/two_vertices_in.rs | 10 +- tests/mock_graph/arbitrary/vertex_in_graph.rs | 51 ++++-- 26 files changed, 252 insertions(+), 189 deletions(-) diff --git a/src/algo/bfs.rs b/src/algo/bfs.rs index bff70dd..9093693 100644 --- a/src/algo/bfs.rs +++ b/src/algo/bfs.rs @@ -1,4 +1,4 @@ -use crate::core::{property::HasVertex, Graph}; +use crate::core::{property::VertexIn, Graph}; use std::collections::VecDeque; /// Performs [breadth-first traversal](https://mathworld.wolfram.com/Breadth-FirstTraversal.html) @@ -83,23 +83,17 @@ where { /// Constructs a new `Bfs` to traverse the specified graph. /// - /// It calls [`get_vertex`] on the graph, making the traversal start from - /// the returned vertex. The first call to [`next`] on the constructed `Bfs` - /// is guaranteed to return the aforementioned vertex. - /// - /// ### Hint - /// - /// [`VertexInGraph`](../core/property/struct.VertexInGraph.html) can be - /// used to select which specific vertex is returned by [`get_vertex`] and - /// thereby the starting vertex for the traversal. + /// It calls [`vertex_at::<0>()`] on the graph, making the traversal start + /// from the returned vertex. The first call to [`next`] on the constructed + /// `Bfs` is guaranteed to return the aforementioned vertex. /// /// [`next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next - /// [`get_vertex`]: ../core/property/trait.HasVertex.html#method.get_vertex + /// [`vertex_at`]: ../core/property/trait.VertexIn.html#method.vertex_at pub fn new(graph: &'a G) -> Self where - G: HasVertex, + G: VertexIn<1>, { - let v = graph.get_vertex(); + let v = graph.vertex_at::<0>(); let mut result = Self { graph, diff --git a/src/algo/dfs.rs b/src/algo/dfs.rs index fff42ea..d3ff6d0 100644 --- a/src/algo/dfs.rs +++ b/src/algo/dfs.rs @@ -1,4 +1,4 @@ -use crate::core::{property::HasVertex, Graph}; +use crate::core::{property::VertexIn, Graph}; use std::borrow::Borrow; /// Performs [depth-first traversal](https://mathworld.wolfram.com/Depth-FirstTraversal.html) @@ -140,9 +140,9 @@ where payload: F, ) -> Self where - G: HasVertex, + G: VertexIn<1>, { - let v = g.get_vertex(); + let v = g.vertex_at::<0>(); let mut result = Self { graph: g, visited: Vec::new(), @@ -230,7 +230,7 @@ where impl<'a, G> Dfs<'a, G, ()> where - G: 'a + HasVertex, + G: 'a + VertexIn<1>, { /// Constructs a new `Dfs` to traverse the specified graph. /// diff --git a/src/algo/dijkstra_shortest_paths.rs b/src/algo/dijkstra_shortest_paths.rs index 7b9d6ce..8aeb79c 100644 --- a/src/algo/dijkstra_shortest_paths.rs +++ b/src/algo/dijkstra_shortest_paths.rs @@ -1,4 +1,4 @@ -use crate::core::{property::HasVertex, Edge, Graph}; +use crate::core::{property::VertexIn, Edge, Graph}; use num_traits::{PrimInt, Unsigned, Zero}; use std::borrow::Borrow; @@ -21,14 +21,14 @@ where { pub fn new(graph: &'a G) -> Self where - G: HasVertex, + G: VertexIn<1>, { let mut dijk = Self { graph, visited: Vec::new(), queue: Vec::new(), }; - dijk.visit(graph.get_vertex(), G::EdgeWeight::zero()); + dijk.visit(graph.vertex_at::<0>(), G::EdgeWeight::zero()); dijk } @@ -66,9 +66,9 @@ where /// weighted distance to them pub fn distances(graph: &'a G) -> impl 'a + Iterator where - G: HasVertex, + G: VertexIn<1>, { - let mut distances = vec![(graph.get_vertex(), G::EdgeWeight::zero())]; + let mut distances = vec![(graph.vertex_at::<0>(), G::EdgeWeight::zero())]; DijkstraShortestPaths::new(graph).map(move |(so, si, w)| { let dist = distances.iter().find(|(v, _)| so == *v).unwrap().1; @@ -114,7 +114,7 @@ where { pub fn new(graph: &'a G) -> Self where - G: HasVertex, + G: VertexIn<1>, { Self { dijk: DijkstraShortestPaths::new(graph), diff --git a/src/algo/mod.rs b/src/algo/mod.rs index bea5f03..de45547 100644 --- a/src/algo/mod.rs +++ b/src/algo/mod.rs @@ -4,7 +4,6 @@ mod bfs; mod dfs; mod dijkstra_shortest_paths; mod tarjan_scc; -mod shortest_path; pub use self::{bfs::*, dfs::*, dijkstra_shortest_paths::*, tarjan_scc::*}; use crate::core::{property::VertexInGraph, Ensure, Graph}; diff --git a/src/algo/tarjan_scc.rs b/src/algo/tarjan_scc.rs index 942edba..ccc199a 100644 --- a/src/algo/tarjan_scc.rs +++ b/src/algo/tarjan_scc.rs @@ -51,7 +51,7 @@ use crate::{ algo::Dfs, core::{ - property::{ConnectedGraph, HasVertex}, + property::{ConnectedGraph, VertexIn}, proxy::SubgraphProxy, Directed, Graph, Guard, }, @@ -82,9 +82,9 @@ use std::cmp::min; /// # algo::TarjanScc, /// # common::AdjListGraph, /// # core::{ -/// # Guard, +/// # Ensure, /// # property::{ -/// # NewVertex, AddEdge, HasVertexGraph, Subgraph +/// # NewVertex, AddEdge, VertexInGraph, Subgraph /// # } /// # }, /// # }; @@ -105,8 +105,7 @@ use std::cmp::min; /// // Connect first SCC to second /// graph.add_edge(&v0,&v2).unwrap(); /// -/// // We use `HasVertexGraph` because we don't care where we start -/// let graph = HasVertexGraph::guard(graph).unwrap(); +/// let graph = VertexInGraph::ensure(graph, [v0]).unwrap(); /// /// // Initialize algorithm /// let mut tarj = TarjanScc::new(&graph); @@ -160,7 +159,7 @@ where impl<'a, G> TarjanScc<'a, G> where - G: 'a + Graph + HasVertex, + G: 'a + Graph + VertexIn<1>, { /// Constructs a new `TarjanScc` to find the [strongly connected components](https://mathworld.wolfram.com/StronglyConnectedComponent.html) /// of the specified graph. @@ -214,7 +213,7 @@ where Dfs::do_nothing_on_visit, on_exit, Dfs::do_nothing_on_explore, - vec![(graph.get_vertex(), 0)], + vec![(graph.vertex_at::<0>(), 0)], ); Self { dfs, diff --git a/src/core/property/connected.rs b/src/core/property/connected.rs index bbf6b8f..9632812 100644 --- a/src/core/property/connected.rs +++ b/src/core/property/connected.rs @@ -3,10 +3,10 @@ use crate::{ core::{ property::{ proxy_remove_edge_where_weight, proxy_remove_vertex, DirectedGraph, EdgeCount, - HasVertex, HasVertexGraph, RemoveEdge, RemoveVertex, Unilateral, VertexInGraph, Weak, + RemoveEdge, RemoveVertex, Unilateral, VertexIn, VertexInGraph, Weak, }, proxy::ReverseGraph, - Ensure, Graph, GraphDerefMut, + Ensure, Graph, GraphDerefMut, Release, }, }; use num_traits::{PrimInt, Unsigned, Zero}; @@ -25,7 +25,7 @@ pub trait Connected: Unilateral /// edge(s) between them. fn eccentricity(&self) -> Self::EdgeWeight where - Self: EdgeCount + HasVertex + Sized, + Self: EdgeCount + VertexIn<1> + Sized, Self::EdgeWeight: PrimInt + Unsigned, { // We search for all the shortest paths, the eccentricity is the longest one @@ -133,8 +133,10 @@ impl Ensure for ConnectedGraph let g = c.graph(); let v_count = g.all_vertices().count(); - if let Ok(g) = HasVertexGraph::ensure(g, ()) + if v_count > 0 { + let v = g.all_vertices().next().unwrap(); + let g = VertexInGraph::ensure_unchecked(g.release(), [v]); let dfs_count = Dfs::new_simple(&g).count(); if (dfs_count + 1) == v_count { diff --git a/src/core/property/has_vertex.rs b/src/core/property/has_vertex.rs index 559cad2..d4747a9 100644 --- a/src/core/property/has_vertex.rs +++ b/src/core/property/has_vertex.rs @@ -1,31 +1,56 @@ -use crate::core::{ - property::{RemoveVertex, Rooted}, - Ensure, Graph, GraphDerefMut, -}; +use crate::core::{property::RemoveVertex, Ensure, Graph, GraphDerefMut, Release}; use std::{ borrow::Borrow, fmt::{Debug, Error, Formatter}, }; /// A marker trait for graphs with at least 1 vertex. +pub trait HasVertex: Graph +{ + /// Return a vertex from the graph. + /// + /// May return different vertices on successive calls to the same unmodified + /// graph. + fn any_vertex(&self) -> Self::Vertex; + + // fn as_vertex_in(self: impl Borrow) -> VertexInGraph + // where + // Self: Ensure, + // ::Graph: Graph + // { + // let v = self.borrow().any_vertex(); + // VertexInGraph::ensure_unchecked(self, [v]) + // } +} + +/// For specifying specific vertices in a graph. +/// +/// This is primarily used as input to various algorithms. E.g. a search +/// algorithm will require a starting vertex and so might take `VertexIn<1>` as +/// input. Finding a path between two vertices could likewise take `VertexIn<2, +/// false>` as input. +/// +/// The specified vertices are ordered and indexed. /// -/// `V` is the number of vertices that are guaranteed to be in the graph. -/// If `Unique` is true (default) then indexed vertices methods will never return duplicate -/// vertices. -pub trait HasVertex: Graph +/// `N` is the number of vertices specified. +/// `UNIQUE` signifies whether there are any duplicates in the vertices. +/// If true, there can be no duplicate vertices. +pub trait VertexIn: HasVertex { - /// Ensures this trait cannot be implemented with V=0. + /// Ensures this trait gets valid parameters. + /// + /// This trait does not accept N=0, or N=1 and !UNIQUE. /// /// Add a call to this associated type in the implementing type's - /// constructor to ensure if that type ever gets v=0, compilation will - /// fail. + /// constructor to ensure if that type ever get invalid parameters, + /// compilation will fail. /// /// Example: /// ```compile_fail, E0080 /// # use std::borrow::Borrow; /// # use graphene::{ /// # common::AdjListGraph, - /// # core::{Directed, Graph, property::HasVertex} + /// # core::{Directed, Graph, property::{VertexIn, HasVertex}} /// # }; /// # impl Graph for Struct { /// # type Vertex = (); @@ -46,17 +71,22 @@ pub trait HasVertex: Graph /// # } /// # /// # } - /// struct Struct(usize); + /// # impl HasVertex for Struct { + /// # fn any_vertex(&self) -> Self::Vertex{ + /// # () + /// # } + /// # } + /// struct Struct(usize); /// - /// impl HasVertex for Struct { - /// fn get_vertex_idx(&self, idx: usize) -> Self::Vertex { + /// impl VertexIn for Struct { + /// fn vertex_at_idx(&self, idx: usize) -> Self::Vertex { /// () /// } /// } /// /// impl Struct { /// fn new() -> Self { - /// _ = Self::ASSERT_NOT_0; // This ensures errors are thrown if V = 0 + /// _ = Self::ASSERT_VALID_PARAMS; // This ensures errors are thrown if V = 0 /// Struct(V) /// } /// } @@ -64,45 +94,39 @@ pub trait HasVertex: Graph /// let _ = Struct::<0>::new(); // Will cause a compile error /// let _ = Struct::<1>::new(); // Will compile successfully /// ``` - const ASSERT_NOT_0: () = assert!(V > 0, "Found type implementing HasVertex<0>"); + const ASSERT_VALID_PARAMS: () = { + assert!(N > 0, "Found type implementing VertexIn<0>"); + assert!( + N != 1 || UNIQUE, + "Found type implementing VertexIn<1, false>" + ); + }; - /// Returns a vertex in the graph. - /// - /// Successive calls do not guarantee to return the same vertex, - /// even though the graph hasn't changed. - fn get_vertex(&self) -> Self::Vertex + /// Returns the I'th vertex specified in the graph. + fn vertex_at(&self) -> Self::Vertex { - _ = Self::ASSERT_NOT_0; - self.get_vertex_at::<0>() - } - - /// Returns the I'th vertex guaranteed to be in the vertex. - /// - /// The vertex ordering (i.e. the I) is significant and does not change unless - /// the underlying graph type changes it. - fn get_vertex_at(&self) -> Self::Vertex { - _ = Self::ASSERT_NOT_0; - const { - assert!(I < V) - } - assert!(I < V, "I out of bounds"); - self.get_vertex_idx(I) + _ = Self::ASSERT_VALID_PARAMS; + const { assert!(I < N) } + self.vertex_at_idx(I) } - - /// Returns vertex guaranteed to be in the vertex at the given index in the ordering. - /// - /// The vertex ordering (i.e. the idx) is significant and does not change unless - /// the underlying graph type changes it. - fn get_vertex_idx(&self, idx: usize) -> Self::Vertex; + + /// Returns the I'th vertex specified in the graph. + fn vertex_at_idx(&self, idx: usize) -> Self::Vertex; } /// Ensures the underlying graph has at least 1 vertex. -/// -/// Gives no guarantees on which vertex is returned by any given call to -/// `get_vertex` if the graph has multiple vertices. #[derive(Clone)] pub struct HasVertexGraph(C); +impl HasVertexGraph +{ + pub fn as_vertex_in(self) -> VertexInGraph + { + let v = self.any_vertex(); + VertexInGraph::ensure_unchecked(self.release(), [v]) + } +} + impl Ensure for HasVertexGraph { fn ensure_unchecked(c: Self::Ensured, _: ()) -> Self @@ -133,11 +157,10 @@ where } } -impl HasVertex for HasVertexGraph +impl HasVertex for HasVertexGraph { - fn get_vertex_idx(&self, idx: usize) -> Self::Vertex + fn any_vertex(&self) -> Self::Vertex { - assert!(idx < V); self.all_vertices() .next() .expect("HasVertexGraph has no vertices.") @@ -153,9 +176,12 @@ impl_ensurer! { /// /// The designated vertices cannot be removed from the graph. #[derive(Clone)] -pub struct VertexInGraph(C, [::Vertex; V]); +pub struct VertexInGraph( + C, + [::Vertex; V], +); -impl VertexInGraph +impl VertexInGraph { /// ```compile_fail, E0080 /// use graphene::common::AdjListGraph; @@ -166,7 +192,7 @@ impl VertexInGraph /// ``` fn new(c: C, vs: [::Vertex; V]) -> Self { - _ = Self::ASSERT_NOT_0; + _ = Self::ASSERT_VALID_PARAMS; Self(c, vs) } @@ -190,7 +216,7 @@ impl VertexInGraph } } -impl Debug for VertexInGraph +impl Debug for VertexInGraph where C: Debug, ::Vertex: Debug, @@ -204,7 +230,7 @@ where } } -impl Ensure for VertexInGraph +impl Ensure for VertexInGraph { fn ensure_unchecked(c: Self::Ensured, v: [::Vertex; V]) -> Self { @@ -217,7 +243,8 @@ impl Ensure for VertexInGraph } } -impl RemoveVertex for VertexInGraph +impl RemoveVertex + for VertexInGraph where C::Graph: RemoveVertex, { @@ -234,32 +261,24 @@ where } } -impl HasVertex for VertexInGraph +impl HasVertex for VertexInGraph { - fn get_vertex_idx(&self, idx: usize) -> Self::Vertex + fn any_vertex(&self) -> Self::Vertex { - assert!(idx < V); - self.1[idx] + self.vertex_at::<0>() } } -impl Rooted for VertexInGraph -where - C::Graph: Rooted, +impl VertexIn for VertexInGraph { - fn root(&self) -> Self::Vertex + fn vertex_at_idx(&self, idx: usize) -> Self::Vertex { - self.get_vertex() - } - - fn set_root(&mut self, v: impl Borrow) -> Result<(), ()> - { - self.set_vertex([*v.borrow()]) + self.1[idx] } } impl_ensurer! { - use VertexInGraph: Ensure, HasVertex, RemoveVertex, Rooted + use VertexInGraph: Ensure, HasVertex, VertexIn, RemoveVertex as (self.0) : C as (self.1) : [::Vertex;V] } diff --git a/src/core/property/impl_ensurer.rs b/src/core/property/impl_ensurer.rs index 8a3cad1..377df1d 100644 --- a/src/core/property/impl_ensurer.rs +++ b/src/core/property/impl_ensurer.rs @@ -588,20 +588,50 @@ macro_rules! impl_properties { $crate::impl_properties!{ @struct [ $struct ] @generic [ $($generics)* ] - @const_generic [ $([$const_gen_id $const_gen_ty])* [IMPL_PROPERTIES_HASVERTEX_V usize] ] + @const_generic [ $([$const_gen_id $const_gen_ty])* ] + @delegate [ $delegate_type ] + $(@exclude [ $($exclude_props)* ])? + $(@include [ $($include_props)* ])? + @bounds [ + <$delegate_type as $crate::core::GraphDeref>::Graph: + $crate::core::property::HasVertex, + $($bounds)* + ] + @trait_id HasVertex [$crate::core::property] + @implement { + delegate::delegate! { + to $crate::core::GraphDeref::graph(&self$($delegate)+) { + fn any_vertex(&self) -> Self::Vertex; + } + } + } + } + + // HasVertex + $crate::impl_properties!{ + @struct [ $struct ] + @generic [ $($generics)* ] + @const_generic [ + $([$const_gen_id $const_gen_ty])* + [IMPL_PROPERTIES_VERTEXIN_N usize] + [IMPL_PROPERTIES_VERTEXIN_U bool] + ] @delegate [ $delegate_type ] $(@exclude [ $($exclude_props)* ])? $(@include [ $($include_props)* ])? @bounds [ <$delegate_type as $crate::core::GraphDeref>::Graph: - $crate::core::property::HasVertex, + $crate::core::property::VertexIn< + IMPL_PROPERTIES_VERTEXIN_N,IMPL_PROPERTIES_VERTEXIN_U + >, $($bounds)* ] - @trait_id HasVertex [$crate::core::property] + @trait_id VertexIn + [$crate::core::property] @implement { delegate::delegate! { to $crate::core::GraphDeref::graph(&self$($delegate)+) { - fn get_vertex_idx(&self, idx: usize) -> Self::Vertex; + fn vertex_at_idx(&self, idx: usize) -> Self::Vertex; } } } diff --git a/src/core/property/rooted.rs b/src/core/property/rooted.rs index 4486833..71982fd 100644 --- a/src/core/property/rooted.rs +++ b/src/core/property/rooted.rs @@ -10,7 +10,7 @@ use std::borrow::Borrow; /// A rooted graph always has a root, which cannot be removed (unless another /// vertex is designated as the root first). /// -/// Even though rooted graphs always implement `HasVertex`, the `get_vertex` +/// Even though rooted graphs always implement `HasVertex`, the `any_vertex` /// method is not required to always return the root of the graph. /// To always get the root, the `root` method can be used. pub trait Rooted: HasVertex @@ -80,7 +80,7 @@ impl Rooted for RootedGraph { fn root(&self) -> Self::Vertex { - self.0.get_vertex() + self.0.any_vertex() } fn set_root(&mut self, v: impl Borrow) -> Result<(), ()> diff --git a/src/core/property/unilateral.rs b/src/core/property/unilateral.rs index 1c4cf97..20cb643 100644 --- a/src/core/property/unilateral.rs +++ b/src/core/property/unilateral.rs @@ -47,6 +47,7 @@ where // order, so we don't need to sort, just check the first has an edge to it from // the next. + let graph = graph.as_vertex_in(); let mut tarjan = TarjanScc::new(&graph); let mut scc_current = tarjan.next(); diff --git a/src/core/proxy/reverse_graph.rs b/src/core/proxy/reverse_graph.rs index fd59f76..039b76e 100644 --- a/src/core/proxy/reverse_graph.rs +++ b/src/core/proxy/reverse_graph.rs @@ -105,7 +105,7 @@ where } base_graph! { - use ReverseGraph: NewVertex, RemoveVertex, HasVertex + use ReverseGraph: NewVertex, RemoveVertex, HasVertex, VertexIn as (self.0): C where C: Ensure, diff --git a/tests/algo/bfs.rs b/tests/algo/bfs.rs index 265dbe1..9cd21be 100644 --- a/tests/algo/bfs.rs +++ b/tests/algo/bfs.rs @@ -3,7 +3,7 @@ use duplicate::duplicate_item; use graphene::{ algo::Bfs, core::{ - property::{ConnectedGraph, HasVertex, VertexInGraph}, + property::{ConnectedGraph, VertexIn, VertexInGraph}, Directed, Undirected, }, }; @@ -44,7 +44,7 @@ mod __ ) -> bool { let mut seen = HashSet::new(); - seen.insert(graph.get_vertex().value); + seen.insert(graph.vertex_at::<0>().value); let mut bfs = Bfs::new(&graph); while let Some(v) = bfs.next() @@ -110,7 +110,7 @@ mod __ Arb(graph): Arb>>>, ) -> bool { - let root = graph.get_vertex(); + let root = graph.vertex_at::<0>(); let mut bfs = Bfs::new(&graph); while let Some(v) = bfs.next() @@ -135,7 +135,7 @@ fn predecessor_path_reaches_root( Arb(graph): Arb>>>, ) -> bool { - let root = graph.get_vertex(); + let root = graph.vertex_at::<0>(); let mut bfs = Bfs::new(&graph); while let Some(v) = bfs.next() diff --git a/tests/algo/dfs.rs b/tests/algo/dfs.rs index 8be2d0e..d176b16 100644 --- a/tests/algo/dfs.rs +++ b/tests/algo/dfs.rs @@ -5,7 +5,7 @@ use duplicate::duplicate_item; use graphene::{ algo::Dfs, core::{ - property::{ConnectedGraph, HasVertex, VertexInGraph}, + property::{ConnectedGraph, VertexIn, VertexInGraph}, Directed, Undirected, }, }; @@ -27,7 +27,7 @@ mod __ { // Ensure the starting vertex is on the stack, so that it is the last // to run 'on_exit' - let stack: Cell> = Cell::new(vec![mock.get_vertex()]); + let stack: Cell> = Cell::new(vec![mock.vertex_at::<0>()]); let mut success = true; fn on_exit( diff --git a/tests/algo/dijkstra_shortest_paths.rs b/tests/algo/dijkstra_shortest_paths.rs index 70156e6..2bec2fa 100644 --- a/tests/algo/dijkstra_shortest_paths.rs +++ b/tests/algo/dijkstra_shortest_paths.rs @@ -5,7 +5,7 @@ use duplicate::duplicate_item; use graphene::{ algo::DijkstraShortestPaths, core::{ - property::{AddEdge, ConnectedGraph, HasVertex, VertexInGraph}, + property::{AddEdge, ConnectedGraph, VertexIn, VertexInGraph}, proxy::EdgeWeightMap, Directed, Ensure, Graph, GraphDeref, ReleasePayload, Undirected, }, @@ -28,7 +28,7 @@ mod __ { // Use a set to ensure we only count each vertex once let mut visited = HashSet::new(); - visited.insert(mock.get_vertex().clone()); + visited.insert(mock.vertex_at::<0>().clone()); let mut visited_once = true; let e_map = EdgeWeightMap::new(mock.graph(), |_, _, w| w.value); DijkstraShortestPaths::new(&e_map).for_each(|(_, v, _)| { @@ -63,7 +63,7 @@ mod __ fn increasing_path_lengths(Arb(g): Arb>>) -> bool { let mut path_weights = HashMap::new(); - path_weights.insert(g.get_vertex().clone(), 0); + path_weights.insert(g.vertex_at::<0>().clone(), 0); let mut len = 0; let e_map = EdgeWeightMap::new(g, |_, _, w| w.value); @@ -86,7 +86,7 @@ mod __ fn path_source_already_seen(Arb(g): Arb>>) -> bool { let mut seen = HashSet::new(); - seen.insert(g.get_vertex().clone()); + seen.insert(g.vertex_at::<0>().clone()); let e_map = EdgeWeightMap::new(g, |_, _, w| w.value); for (source, sink, _) in DijkstraShortestPaths::new(&e_map) diff --git a/tests/algo/search.rs b/tests/algo/search.rs index 6d865ac..5b90ef2 100644 --- a/tests/algo/search.rs +++ b/tests/algo/search.rs @@ -5,7 +5,7 @@ use duplicate::duplicate_item; use graphene::{ algo::{Bfs, Dfs, Spfs}, core::{ - property::{AddEdge, ConnectedGraph, HasVertex, VertexInGraph}, + property::{AddEdge, ConnectedGraph, VertexIn, VertexInGraph}, proxy::EdgeWeightMap, Directed, Ensure, Graph, GraphDeref, ReleasePayload, Undirected, }, @@ -45,7 +45,7 @@ mod module let mut visited = HashSet::new(); // Add the starting vertex to ensure it is not produced. - visited.insert(mock.get_vertex().clone()); + visited.insert(mock.vertex_at::<0>().clone()); let mut visited_once = true; search_algo_new([mock.graph()]).for_each(|v| { diff --git a/tests/common/adjacency_list/impl_graph.rs b/tests/common/adjacency_list/impl_graph.rs index cc63e9c..d38c589 100644 --- a/tests/common/adjacency_list/impl_graph.rs +++ b/tests/common/adjacency_list/impl_graph.rs @@ -10,7 +10,7 @@ use crate::{ }; use duplicate::duplicate_item; use graphene::core::{ - property::{EdgeCount, HasVertex, RemoveEdge, RemoveVertex, VertexCount, VertexInGraph}, + property::{EdgeCount, RemoveEdge, RemoveVertex, VertexCount, VertexIn, VertexInGraph}, Directed, Graph, GraphMut, Release, Undirected, }; @@ -55,7 +55,7 @@ mod __ #[quickcheck] fn same_vertex_weight_mut(Arb(mock): Arb>>) -> bool { - let v = mock.get_vertex().clone(); + let v = mock.vertex_at::<0>().clone(); let (mut g, v_map) = adj_list_from_mock(&mock.release_all()); g.vertex_weight(&v_map[&v]).map(|w| w as *const _) @@ -106,7 +106,7 @@ mod __ #[quickcheck] fn remove_vertex(Arb(mock): Arb>>) -> bool { - let v_remove = mock.get_vertex().clone(); + let v_remove = mock.vertex_at::<0>().clone(); let mock = mock.release_all(); let (mut g, v_map) = adj_list_from_mock(&mock); let v_removed = v_map[&v_remove]; @@ -144,7 +144,7 @@ mod __ fn remove_edge(Arb(mock): Arb>>) -> bool { let (mut g, v_map) = adj_list_from_mock(&mock); - let source = mock.get_vertex(); + let source = mock.vertex_at::<0>(); let EdgeIn(mock, sink, weight) = mock; let mapped_source = v_map[&source]; diff --git a/tests/common/ensured.rs b/tests/common/ensured.rs index 23d60fe..2ee72a0 100644 --- a/tests/common/ensured.rs +++ b/tests/common/ensured.rs @@ -28,7 +28,7 @@ fn contains_vertex(Arb(graph): Arb>, v: MockVertex) -> bool let expected = graph.contains_vertex(v); let result = graph_clone.ensured().contains_vertex(v); let result_is_some = result.is_some(); - let result_v = result.map_or(v, |graph| graph.get_vertex()); + let result_v = result.map_or(v, |graph| graph.any_vertex()); (expected == result_is_some) && (v == result_v) } @@ -50,7 +50,7 @@ fn new_vertex(Arb(mut graph): Arb>, _w: MockVertexWeight) -> let expected_v = expected.unwrap_or(MockVertex { value: 0 }); let result = graph_clone.ensured().new_vertex(arguments); let result_is_ok = result.is_ok(); - let result_v = result.map_or(expected_v, |graph| graph.get_vertex()); + let result_v = result.map_or(expected_v, |graph| graph.any_vertex()); (expected_is_ok == result_is_ok) && (expected_v == result_v) } diff --git a/tests/core/graph/edge_lookup_methods.rs b/tests/core/graph/edge_lookup_methods.rs index a355ff4..b6fce58 100644 --- a/tests/core/graph/edge_lookup_methods.rs +++ b/tests/core/graph/edge_lookup_methods.rs @@ -6,7 +6,7 @@ use crate::mock_graph::{ }; use duplicate::duplicate_item; use graphene::core::{ - property::{HasVertex, VertexInGraph}, + property::{VertexIn, VertexInGraph}, Directed, Edge, Graph, Release, Undirected, }; @@ -34,7 +34,7 @@ use crate::mock_graph::arbitrary::{Arb, TwoVerticesIn}; module [ module_nested ] method [ method_nested ] vertices [ &v ] - vertices_init [ let v = g.get_vertex().clone() ] + vertices_init [ let v = g.vertex_at::<0>().clone() ] vertices_init_invalid [ let v = g.1 ] closure [ |e| if closure_nested {Some((e.other(v),e.2))} else {None} ] arb_graph [ VertexInGraph ] diff --git a/tests/core/property/acyclic.rs b/tests/core/property/acyclic.rs index e2885d1..341ef81 100644 --- a/tests/core/property/acyclic.rs +++ b/tests/core/property/acyclic.rs @@ -6,7 +6,7 @@ use crate::mock_graph::{ }; use duplicate::duplicate_item; use graphene::core::{ - property::{Acyclic, AcyclicGraph, AddEdge, HasVertex, NoLoops, RemoveEdge}, + property::{Acyclic, AcyclicGraph, AddEdge, NoLoops, RemoveEdge, VertexIn}, Directed, Graph, Guard, Release, Undirected, }; use static_assertions::assert_impl_all; @@ -38,7 +38,7 @@ mod __ #[quickcheck] fn accept_add_edge(Arb(g): Arb>>>) -> bool { - let source = g.get_vertex(); + let source = g.vertex_at::<0>(); let sink = g.1; let mut g = g.release_all(); diff --git a/tests/core/property/connectedness.rs b/tests/core/property/connectedness.rs index f52ae46..1e7f8b4 100644 --- a/tests/core/property/connectedness.rs +++ b/tests/core/property/connectedness.rs @@ -9,8 +9,8 @@ use graphene::{ algo::DijkstraShortestPaths, core::{ property::{ - AddEdge, Connected, ConnectedGraph, HasVertex, NewVertex, RemoveEdge, RemoveVertex, - Unilateral, UnilateralGraph, VertexInGraph, Weak, WeakGraph, + AddEdge, Connected, ConnectedGraph, NewVertex, RemoveEdge, RemoveVertex, Unilateral, + UnilateralGraph, VertexIn, VertexInGraph, Weak, WeakGraph, }, Directed, Graph, Undirected, }, @@ -109,8 +109,8 @@ mod module e_weight: MockEdgeWeight, ) -> bool { - let v1 = g1.get_vertex().clone(); - let v2 = g2.get_vertex().clone(); + let v1 = g1.vertex_at::<0>().clone(); + let v2 = g2.vertex_at::<0>().clone(); let mut graph = g1.release_all(); // We start by joining 2 connected graphs into a unconnected graph with the 2 // components diff --git a/tests/core/property/has_vertex_rooted.rs b/tests/core/property/has_vertex_rooted.rs index 28833d3..9c5f242 100644 --- a/tests/core/property/has_vertex_rooted.rs +++ b/tests/core/property/has_vertex_rooted.rs @@ -31,7 +31,7 @@ mod __ assert!(!HasVertexGraph::can_guard(&null_graph)); } - + #[duplicate_item( mod_name has_num; [__1] [ 1 ]; @@ -43,22 +43,26 @@ mod __ mod __ { use super::*; - + /// Tests that graphs with at least has_num>0 vertices are accepted. #[quickcheck] - fn accept_has_vertex(Arb(g): Arb,has_num>>) -> bool + fn accept_has_vertex( + Arb(g): Arb, has_num>>, + ) -> bool { HasVertexGraph::can_guard(&g.release_all()) } - + /// Tests that graphs that already implement HasVertex0>. #[quickcheck] - fn accept_has_vertex_implemented(Arb(g): Arb,has_num>>) -> bool + fn accept_has_vertex_implemented( + Arb(g): Arb, has_num>>, + ) -> bool { HasVertexGraph::can_guard(&g) } } - + /// Tests cannot remove a vertex if it's the only one in the graph. #[test] fn reject_remove_vertex() @@ -67,30 +71,30 @@ mod __ let v = g .new_vertex_weighted(MockVertexWeight { value: 0 }) .unwrap(); - + let mut g = HasVertexGraph::guard(g).unwrap(); - + assert!(g.remove_vertex(v).is_err()) } - + /// Tests that can remove a vertex if there are at least 2. #[quickcheck] fn accept_remove_vertex(Arb(g): Arb, Unique>>) -> bool { - let v = g.get_vertex(); + let v = g.any_vertex(); let mut g = HasVertexGraph::guard(g.release_all()).unwrap(); - + g.remove_vertex(v).is_ok() } } #[duplicate_item( - GraphStruct get_method set_method ensure_wrap(v); - [ VertexInGraph ] [ get_vertex ] [ set_vertex ] [[v]]; - [ RootedGraph ] [ root ] [ set_root ] [v]; + mod_name GraphStruct get_method set_method ensure_wrap(v); + [vertex_in] [ VertexInGraph::<_,1,true> ] [ any_vertex ] [ set_vertex ] [[v]]; + [rooted_graph] [ RootedGraph ] [ root ] [ set_root ] [v]; )] - mod __ + mod mod_name { use super::*; use crate::mock_graph::arbitrary::VertexOutside; @@ -100,7 +104,7 @@ mod __ #[quickcheck] fn accept_in_graph(Arb(g): Arb>>) -> bool { - GraphStruct::can_ensure(&g, &ensure_wrap([g.get_vertex()])) + GraphStruct::can_ensure(&g, &ensure_wrap([g.any_vertex()])) } /// Tests that vertices not in the graph are rejected. @@ -128,7 +132,7 @@ mod __ #[quickcheck] fn reject_remove_vertex(Arb(g): Arb>>) -> bool { - let v = g.get_vertex(); + let v = g.any_vertex(); let mut g = GraphStruct::ensure_unchecked(g, ensure_wrap([v])); g.remove_vertex(v).is_err() @@ -138,7 +142,7 @@ mod __ #[quickcheck] fn get_vertex(Arb(g): Arb>>) -> bool { - let v = g.get_vertex(); + let v = g.any_vertex(); let g = GraphStruct::ensure_unchecked(g.release_all().0, ensure_wrap([v])); g.get_method() == v @@ -162,7 +166,7 @@ mod __ Arb(g): Arb>>>, ) -> bool { - let v1 = g.0.get_vertex(); + let v1 = g.0.any_vertex(); let v2 = g.1; let mut g = GraphStruct::ensure_unchecked(g.release_all().0, ensure_wrap([v1])); @@ -175,7 +179,7 @@ mod __ fn is_root_true(Arb(g): Arb>>) -> bool { use graphene::core::{Ensure, Release}; - let v = g.get_vertex(); + let v = g.any_vertex(); let g = RootedGraph::ensure_unchecked(g.release_all(), v); g.is_root(v) diff --git a/tests/core/property/unique.rs b/tests/core/property/unique.rs index 6248b30..79dfe4e 100644 --- a/tests/core/property/unique.rs +++ b/tests/core/property/unique.rs @@ -49,7 +49,7 @@ mod __ e_weight: edge_type, ) -> bool { - let v = g.get_vertex().clone(); + let v = g.any_vertex().clone(); // To ensure we add a non-duplicate edge, // we create a new vertex and add an edge to it from an existing one. let v2 = g.new_vertex_weighted(v_weight).unwrap(); @@ -64,7 +64,7 @@ mod __ weight: edge_type, ) -> bool { - let source = g.get_vertex(); + let source = g.any_vertex(); let EdgeIn(mut g, sink, _) = g; g.add_edge_weighted(source, sink, weight).is_err() } diff --git a/tests/mock_graph/arbitrary/acyclic_graph.rs b/tests/mock_graph/arbitrary/acyclic_graph.rs index 5aa3f52..2cb554c 100644 --- a/tests/mock_graph/arbitrary/acyclic_graph.rs +++ b/tests/mock_graph/arbitrary/acyclic_graph.rs @@ -5,7 +5,7 @@ use crate::mock_graph::{ use graphene::{ algo::Dfs, core::{ - property::{AcyclicGraph, AddEdge, HasVertex, VertexInGraph}, + property::{AcyclicGraph, AddEdge, VertexIn, VertexInGraph}, Directedness, Graph, Guard, Release, }, impl_ensurer, @@ -101,13 +101,13 @@ impl GuidedArbGraph for CyclicGraph let mut graph = VertexInGraph::>::arbitrary_fixed(g, v_count, e_count); let mut reachable: Vec<_> = Dfs::new_simple(&graph).collect(); - reachable.push(graph.get_vertex()); // not added by DFS + reachable.push(graph.vertex_at::<0>()); // not added by DFS // Add random edge back to the beginning graph .add_edge_weighted( reachable[g.gen_range(0, reachable.len())], - graph.get_vertex(), + graph.vertex_at::<0>(), MockEdgeWeight::arbitrary(g), ) .unwrap(); diff --git a/tests/mock_graph/arbitrary/combinations/edge_in.rs b/tests/mock_graph/arbitrary/combinations/edge_in.rs index 56315a3..5eeb6e8 100644 --- a/tests/mock_graph/arbitrary/combinations/edge_in.rs +++ b/tests/mock_graph/arbitrary/combinations/edge_in.rs @@ -4,7 +4,7 @@ use crate::mock_graph::{ }; use graphene::{ core::{ - property::{AddEdge, HasVertex, RemoveEdge, VertexInGraph}, + property::{AddEdge, RemoveEdge, VertexIn, VertexInGraph}, Directedness, Graph, GraphDerefMut, GraphMut, }, impl_ensurer, @@ -36,7 +36,7 @@ where fn ensure_unchecked(c: Self::Ensured, _: ()) -> Self { let (sink, weight) = { - let edge = c.edges_sourced_in(c.get_vertex()).next().unwrap(); + let edge = c.edges_sourced_in(c.vertex_at::<0>()).next().unwrap(); (edge.0, edge.1.borrow().clone()) }; Self(c, sink, weight) @@ -104,7 +104,7 @@ where fn shrink_guided(&self, limits: HashSet) -> Box> { - let v1 = self.get_vertex(); + let v1 = self.vertex_at::<0>(); let v2 = self.1; let mut result = Vec::new(); diff --git a/tests/mock_graph/arbitrary/combinations/two_vertices_in.rs b/tests/mock_graph/arbitrary/combinations/two_vertices_in.rs index d1f478e..ffa35d0 100644 --- a/tests/mock_graph/arbitrary/combinations/two_vertices_in.rs +++ b/tests/mock_graph/arbitrary/combinations/two_vertices_in.rs @@ -4,7 +4,7 @@ use crate::mock_graph::{ }; use graphene::{ core::{ - property::{HasVertex, VertexInGraph}, + property::{VertexIn, VertexInGraph}, Ensure, Graph, GraphDeref, Release, }, impl_ensurer, @@ -82,7 +82,7 @@ where pub fn get_both(&self) -> (MockVertex, MockVertex) { - (self.0.get_vertex(), self.1) + (self.0.vertex_at::<0>(), self.1) } pub fn get_two_vertices(g: &mut Ge, graph: &G) -> (MockVertex, MockVertex) @@ -123,7 +123,7 @@ where } else { - verts.filter(|&v| v != c.get_vertex()).next().unwrap() + verts.filter(|&v| v != c.vertex_at::<0>()).next().unwrap() } }; Self(c, v2, PhantomData) @@ -191,10 +191,10 @@ where .map(|g| Self(g, self.1, PhantomData)), ); - if !U::unique() && self.get_vertex() != self.1 + if !U::unique() && self.vertex_at::<0>() != self.1 { // Shrink by making both vertices the same - result.push(Self(self.0.clone(), self.get_vertex(), PhantomData)); + result.push(Self(self.0.clone(), self.vertex_at::<0>(), PhantomData)); result.push(Self( VertexInGraph::ensure(self.0.clone().release(), [self.1]).unwrap(), self.1, diff --git a/tests/mock_graph/arbitrary/vertex_in_graph.rs b/tests/mock_graph/arbitrary/vertex_in_graph.rs index 0196b9e..7c28fe6 100644 --- a/tests/mock_graph/arbitrary/vertex_in_graph.rs +++ b/tests/mock_graph/arbitrary/vertex_in_graph.rs @@ -2,15 +2,12 @@ use crate::mock_graph::{ arbitrary::{GuidedArbGraph, Limit}, MockType, TestGraph, }; -use graphene::core::{ - property::{HasVertex, VertexInGraph}, - Ensure, Graph, Release, -}; +use graphene::core::{property::VertexInGraph, Ensure, Graph, ReleasePayload}; use quickcheck::Gen; use rand::Rng; use std::collections::HashSet; -impl GuidedArbGraph for VertexInGraph +impl GuidedArbGraph for VertexInGraph where Gr::Graph: TestGraph, ::EdgeWeight: MockType, @@ -23,36 +20,54 @@ where e_max: usize, ) -> (usize, usize) { - assert!(v_max > V); + assert!(!U || v_max > V); Gr::choose_size(g, std::cmp::max(v_min, V), v_max, e_min, e_max) } fn arbitrary_fixed(g: &mut G, v_count: usize, e_count: usize) -> Self { - assert!(v_count >= V); + assert!(!U || v_count >= V); let graph = Gr::arbitrary_fixed(g, v_count, e_count); - + // Collect V vertices through elimination - let mut all_vs : Vec<_> = graph .graph().all_vertices().collect(); - while all_vs.len() > V { + let mut all_vs: Vec<_> = graph.graph().all_vertices().collect(); + while all_vs.len() > V + { let remove_idx = g.gen_range(0, all_vs.len()); - all_vs.remove(remove_idx); + if U + { + all_vs.remove(remove_idx); + } } - - let final_vs : [_;V] = all_vs.try_into().unwrap(); + + let final_vs: [_; V] = all_vs.try_into().unwrap(); Self::ensure_unchecked(graph, final_vs) } fn shrink_guided(&self, mut limits: HashSet) -> Box> { - let vs: [_;V]= (0..V).map(|i| self.get_vertex_idx(i)).collect::>().try_into().unwrap(); - vs.iter().for_each(|v| {limits.insert(Limit::VertexKeep(*v));}); + let (g, vs) = self.clone().release(); + vs.iter().for_each(|v| { + limits.insert(Limit::VertexKeep(*v)); + }); Box::new( - self.clone() - .release() + // Shrink without changing the designated vertices + g .shrink_guided(limits) - .map(move |g| Self::ensure_unchecked(g, vs)), + .map(move |g| Self::ensure_unchecked(g, vs)) + // If non-unique, make a vertex equal it predecessor + .chain({ + // Collect vertices + let (g, vs) = self.clone().release(); + (1..V).filter(|_| !U).map(move|i| { + let mut vs_clone = vs.clone(); + if vs[i] != vs[i-1] { + vs_clone[i] = vs[i-1] + } + Self::ensure_unchecked(g.clone(), vs_clone) + }) + }), ) } }