From 2e44c1fb0faa6562a92e4859eaed87e85666150b Mon Sep 17 00:00:00 2001 From: Emad Jacob Maroun Date: Sun, 6 Dec 2020 21:42:37 +0100 Subject: [PATCH] Added core::property::Rooted and acompanying ensurer graph. --- src/core/property/has_vertex.rs | 18 +- src/core/property/impl_ensurer.rs | 27 +++ src/core/property/mod.rs | 4 +- src/core/property/rooted.rs | 95 ++++++++++ tests/core/property/has_vertex.rs | 111 ----------- tests/core/property/has_vertex_rooted.rs | 174 ++++++++++++++++++ tests/core/property/mod.rs | 2 +- .../arbitrary/combinations/vertex_outside.rs | 11 ++ 8 files changed, 328 insertions(+), 114 deletions(-) create mode 100644 src/core/property/rooted.rs delete mode 100644 tests/core/property/has_vertex.rs create mode 100644 tests/core/property/has_vertex_rooted.rs diff --git a/src/core/property/has_vertex.rs b/src/core/property/has_vertex.rs index 4c64e6c..92858a0 100644 --- a/src/core/property/has_vertex.rs +++ b/src/core/property/has_vertex.rs @@ -23,7 +23,7 @@ pub trait HasVertex: Graph /// /// Gives no guarantees on which vertex is returned by any given call to /// `get_vertex` if the the graph has multiple vertices. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct HasVertexGraph(C); impl Ensure for HasVertexGraph @@ -78,6 +78,22 @@ impl_ensurer! { #[derive(Clone)] pub struct VertexInGraph(C, ::Vertex); +impl VertexInGraph +{ + pub fn set_vertex(&mut self, v: impl Borrow<::Vertex>) -> Result<(), ()> + { + if self.0.graph().contains_vertex(v.borrow()) + { + self.1 = v.borrow().clone(); + Ok(()) + } + else + { + Err(()) + } + } +} + impl Debug for VertexInGraph where C: Debug, diff --git a/src/core/property/impl_ensurer.rs b/src/core/property/impl_ensurer.rs index 8d2f87a..8c47040 100644 --- a/src/core/property/impl_ensurer.rs +++ b/src/core/property/impl_ensurer.rs @@ -539,6 +539,33 @@ macro_rules! impl_properties { } } } + + // Rooted + $crate::impl_properties!{ + @struct [ $struct ] + @generic [ $($generics)* ] + @delegate [ $delegate_type ] + $(@exclude [ $($exclude_props)* ])? + $(@include [ $($include_props)* ])? + @bounds [ + $delegate_type: $crate::core::GraphDerefMut, + <$delegate_type as $crate::core::GraphDeref>::Graph: + $crate::core::property::Rooted, + $($bounds)* + ] + @trait_id Rooted [$crate::core::property] + @implement { + delegate::delegate! { + to $crate::core::GraphDeref::graph(&self$($delegate)+) { + fn root(&self) -> Self::Vertex; + } + to $crate::core::GraphDerefMut::graph_mut(&mut self$($delegate)+) { + fn set_root(&mut self, v: impl std::borrow::Borrow) + -> std::result::Result<(), ()>; + } + } + } + } }; { diff --git a/src/core/property/mod.rs b/src/core/property/mod.rs index ebbb6af..fa50723 100644 --- a/src/core/property/mod.rs +++ b/src/core/property/mod.rs @@ -7,6 +7,7 @@ mod directedness_ensurers; mod has_vertex; mod no_loops; mod reflexive; +mod rooted; mod subgraph; mod unilateral; mod unique; @@ -14,7 +15,8 @@ mod weak; pub use self::{ acyclic::*, base_props::*, connected::*, directedness_ensurers::*, has_vertex::*, - impl_ensurer::*, no_loops::*, reflexive::*, subgraph::*, unilateral::*, unique::*, weak::*, + impl_ensurer::*, no_loops::*, reflexive::*, rooted::*, subgraph::*, unilateral::*, unique::*, + weak::*, }; use crate::core::{ proxy::{EdgeProxyGraph, ProxyVertex, VertexProxyGraph}, diff --git a/src/core/property/rooted.rs b/src/core/property/rooted.rs new file mode 100644 index 0000000..06bec3d --- /dev/null +++ b/src/core/property/rooted.rs @@ -0,0 +1,95 @@ +use crate::core::{ + property::{HasVertex, VertexInGraph}, + Ensure, Graph, Release, +}; +use std::borrow::Borrow; + +/// A maker trait for graphs that are rooted. +/// +/// A rooted graph has a distinguished node, called the root of the graph. +/// 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` +/// 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 +{ + /// Returns the root of the graph. + /// + /// If the root of the graph changes between successive calls to this + /// method, so does the vertex returned. + /// However, if the root doesn't change, the vertex is guaranteed to be + /// returned by successive calls. + fn root(&self) -> Self::Vertex; + + /// Designates the given vertex is the root of the graph. + /// + /// Returns error if it was unable to change the root of the graph. + /// E.g. if the given vertex is not in the graph. + fn set_root(&mut self, v: impl Borrow) -> Result<(), ()>; + + /// Return true of the given vertex is the root of the graph. + /// Otherwise returns false. + fn is_root(&self, v: impl Borrow) -> bool + { + self.root() == *v.borrow() + } +} + +/// Ensures the a specific vertex is the root of the graph. +pub struct RootedGraph(VertexInGraph); + +impl Clone for RootedGraph +where + VertexInGraph: Clone, +{ + fn clone(&self) -> Self + { + Self(self.0.clone()) + } +} + +impl Release for RootedGraph +{ + type Base = C::Base; + type Ensured = C; + type Payload = (::Vertex, C::Payload); + + fn release(self) -> (Self::Ensured, ::Vertex) + { + self.0.release() + } +} + +impl Ensure for RootedGraph +{ + fn ensure_unvalidated(c: Self::Ensured, v: ::Vertex) -> Self + { + Self(VertexInGraph::ensure_unvalidated(c, v)) + } + + fn validate(c: &Self::Ensured, p: &::Vertex) -> bool + { + VertexInGraph::::validate(c, p) + } +} + +impl Rooted for RootedGraph +{ + fn root(&self) -> Self::Vertex + { + self.0.get_vertex() + } + + fn set_root(&mut self, v: impl Borrow) -> Result<(), ()> + { + self.0.set_vertex(v) + } +} + +impl_ensurer! { + use RootedGraph: Release, Ensure, Rooted + as (self.0) : VertexInGraph + where C: Ensure +} diff --git a/tests/core/property/has_vertex.rs b/tests/core/property/has_vertex.rs deleted file mode 100644 index 38deef9..0000000 --- a/tests/core/property/has_vertex.rs +++ /dev/null @@ -1,111 +0,0 @@ -/// Tests `HasVertexGraph` and `VertexInGraph` -use crate::mock_graph::arbitrary::Unique; -use crate::mock_graph::{ - arbitrary::{Arb, TwoVerticesIn}, - MockDirectedness, MockGraph, MockVertexWeight, -}; -use duplicate::duplicate; -use graphene::core::{ - property::{HasVertex, HasVertexGraph, NewVertex, RemoveVertex, VertexInGraph}, - Directed, Undirected, -}; - -#[duplicate( - directedness; [ Directed ]; [ Undirected ] -)] -mod __ -{ - use super::*; - mod non_null - { - use super::*; - use graphene::core::{EnsureUnloaded, ReleaseUnloaded}; - - /// Tests that null graphs are rejected. - #[test] - fn reject_null() - { - let null_graph = MockGraph::::empty(); - - assert!(!HasVertexGraph::validate(&null_graph)); - } - - /// Tests that graphs with at least 1 vertex are accepted. - #[quickcheck] - fn accept_non_null(Arb(g): Arb>>) -> bool - { - HasVertexGraph::validate(&g.release_all()) - } - - /// Tests cannot remove a vertex if its the only one in the graph. - #[test] - fn reject_remove_vertex() - { - let mut g = MockGraph::::empty(); - let v = g - .new_vertex_weighted(MockVertexWeight { value: 0 }) - .unwrap(); - - let mut g = HasVertexGraph::ensure(g).unwrap(); - - assert!(g.remove_vertex(v).is_err()) - } - - /// Tests that can remove a vertex if there are at least 2. - #[quickcheck] - fn non_null_accept_remove_vertex( - Arb(g): Arb, Unique>>, - ) -> bool - { - let v = g.get_vertex(); - let mut g = HasVertexGraph::ensure(g.release_all()).unwrap(); - - g.remove_vertex(v).is_ok() - } - } - - mod vertex_in - { - use super::*; - use crate::mock_graph::arbitrary::VertexOutside; - use graphene::core::{Ensure, Release}; - - /// Tests that graphs with at least 1 vertex are accepted. - #[quickcheck] - fn accept_in_graph(Arb(g): Arb>>) -> bool - { - VertexInGraph::validate(&g, &g.get_vertex()) - } - - /// Tests that vertices not in the graph are rejected. - #[quickcheck] - fn reject_not_in_graph(Arb(g): Arb>>) -> bool - { - !VertexInGraph::validate(&g.0, &g.1) - } - - /// Tests that can remove a vertex if its not the one guaranteed by - /// VertexInGraph - #[quickcheck] - fn vertex_in_accept_remove_vertex( - Arb(g): Arb, Unique>>, - ) -> bool - { - let (v1, v2) = g.get_both(); - let mut g = VertexInGraph::ensure_unvalidated(g.release_all().0, v1); - - g.remove_vertex(v2).is_ok() - } - - /// Tests cannot remove a vertex if its the one guaranteed by - /// VertexInGraph - #[quickcheck] - fn reject_remove_vertex(Arb(g): Arb>>) - { - let v = g.get_vertex(); - let mut g = VertexInGraph::ensure_unvalidated(g, v); - - assert!(g.remove_vertex(v).is_err()) - } - } -} diff --git a/tests/core/property/has_vertex_rooted.rs b/tests/core/property/has_vertex_rooted.rs new file mode 100644 index 0000000..02283a4 --- /dev/null +++ b/tests/core/property/has_vertex_rooted.rs @@ -0,0 +1,174 @@ +/// Tests `HasVertexGraph`, VertexInGraph`, and `RootedGraph` +use crate::mock_graph::arbitrary::Unique; +use crate::mock_graph::{ + arbitrary::{Arb, TwoVerticesIn}, + MockGraph, MockVertexWeight, +}; +use duplicate::duplicate; +use graphene::core::{ + property::{ + HasVertex, HasVertexGraph, NewVertex, RemoveVertex, Rooted, RootedGraph, VertexInGraph, + }, + Directed, Undirected, +}; + +#[duplicate( + directedness; [ Directed ]; [ Undirected ] +)] +mod __ +{ + use super::*; + mod has_vertex + { + use super::*; + use graphene::core::{EnsureUnloaded, ReleaseUnloaded}; + + /// Tests that null graphs are rejected. + #[test] + fn reject_null() + { + let null_graph = MockGraph::::empty(); + + assert!(!HasVertexGraph::validate(&null_graph)); + } + + /// Tests that graphs with at least 1 vertex are accepted. + #[quickcheck] + fn accept_has_vertex(Arb(g): Arb>>) -> bool + { + HasVertexGraph::validate(&g.release_all()) + } + + /// Tests cannot remove a vertex if it's the only one in the graph. + #[test] + fn reject_remove_vertex() + { + let mut g = MockGraph::::empty(); + let v = g + .new_vertex_weighted(MockVertexWeight { value: 0 }) + .unwrap(); + + let mut g = HasVertexGraph::ensure(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 mut g = HasVertexGraph::ensure(g.release_all()).unwrap(); + + g.remove_vertex(v).is_ok() + } + } + + #[duplicate( + GraphStruct get_method set_method; + [ VertexInGraph ] [ get_vertex ] [ set_vertex ]; + [ RootedGraph ] [ root ] [ set_root ] + )] + mod __ + { + use super::*; + use crate::mock_graph::arbitrary::VertexOutside; + use graphene::core::{Ensure, Release}; + + /// Tests that graphs with at least 1 vertex are accepted. + #[quickcheck] + fn accept_in_graph(Arb(g): Arb>>) -> bool + { + GraphStruct::validate(&g, &g.get_vertex()) + } + + /// Tests that vertices not in the graph are rejected. + #[quickcheck] + fn reject_not_in_graph(Arb(g): Arb>>) -> bool + { + !GraphStruct::validate(&g.0, &g.1) + } + + /// Tests that can remove a vertex if its not the one guaranteed by + /// the graph + #[quickcheck] + fn vertex_in_accept_remove_vertex( + Arb(g): Arb, Unique>>, + ) -> bool + { + let (v1, v2) = g.get_both(); + let mut g = GraphStruct::ensure_unvalidated(g.release_all().0, v1); + + g.remove_vertex(v2).is_ok() + } + + /// Tests cannot remove a vertex if its the one guaranteed by + /// the graph + #[quickcheck] + fn reject_remove_vertex(Arb(g): Arb>>) -> bool + { + let v = g.get_vertex(); + let mut g = GraphStruct::ensure_unvalidated(g, v); + + g.remove_vertex(v).is_err() + } + + /// Tests the graph can get the underlying vertex + #[quickcheck] + fn get_vertex(Arb(g): Arb>>) -> bool + { + let v = g.get_vertex(); + let g = GraphStruct::ensure_unvalidated(g.release_all().0, v); + + g.get_method() == v + } + + /// Tests that the graph can change the specific underlying + /// vertex + #[quickcheck] + fn set_vertex(Arb(g): Arb, Unique>>) -> bool + { + let (v1, v2) = g.get_both(); + let mut g = GraphStruct::ensure_unvalidated(g.release_all().0, v1); + + g.set_method(v2).is_ok() && g.get_method() == v2 + } + + /// Tests that the graph rejects changing the underlying vertex + /// to one that isn't in the graph. + #[quickcheck] + fn set_vertex_wrong( + Arb(g): Arb>>>, + ) -> bool + { + let v1 = g.0.get_vertex(); + let v2 = g.1; + let mut g = GraphStruct::ensure_unvalidated(g.release_all().0, v1); + + g.set_method(v2).is_err() + } + } + + /// Tests that RootedGraphs `is_root` returns true if given the root + #[quickcheck] + fn is_root_true(Arb(g): Arb>>) -> bool + { + use graphene::core::{Ensure, ReleaseUnloaded}; + let v = g.get_vertex(); + let g = RootedGraph::ensure_unvalidated(g.release_all(), v); + + g.is_root(v) + } + + /// Tests that RootedGraphs `is_root` returns false when not given the root + #[quickcheck] + fn is_root_false(Arb(g): Arb, Unique>>) -> bool + { + use graphene::core::{Ensure, ReleaseUnloaded}; + let (v1, v2) = g.get_both(); + let g = RootedGraph::ensure_unvalidated(g.release_all(), v1); + + !g.is_root(v2) + } +} diff --git a/tests/core/property/mod.rs b/tests/core/property/mod.rs index ec1944e..a9e86a1 100644 --- a/tests/core/property/mod.rs +++ b/tests/core/property/mod.rs @@ -2,5 +2,5 @@ mod acyclic; mod connectedness; -mod has_vertex; +mod has_vertex_rooted; mod unique; diff --git a/tests/mock_graph/arbitrary/combinations/vertex_outside.rs b/tests/mock_graph/arbitrary/combinations/vertex_outside.rs index 344900e..ff677a5 100644 --- a/tests/mock_graph/arbitrary/combinations/vertex_outside.rs +++ b/tests/mock_graph/arbitrary/combinations/vertex_outside.rs @@ -45,6 +45,17 @@ where Gr: GuidedArbGraph, Gr::Graph: TestGraph, { + fn choose_size( + g: &mut G, + v_min: usize, + v_max: usize, + e_min: usize, + e_max: usize, + ) -> (usize, usize) + { + Gr::choose_size(g, v_min, v_max, e_min, e_max) + } + fn arbitrary_fixed(g: &mut G, v_count: usize, e_count: usize) -> Self { let graph = Gr::arbitrary_fixed(g, v_count, e_count);