Skip to content

Commit d89b08d

Browse files
committed
Added 'Simple' property
1 parent 05acd06 commit d89b08d

20 files changed

+360
-119
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ delegate = "0.13.1"
2323
tt-equal = "0.1"
2424
tt-call = "1.0"
2525
num-traits = "0.2"
26+
duplicate = "2.0.0"
2627

2728
[dev-dependencies]
2829
rand = "0.7"
2930
quickcheck = "0.9"
3031
quickcheck_macros = "0.9"
3132
static_assertions = "1.1.0"
32-
duplicate = "2.0.0"

src/core/property/impl_ensurer.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
///
66
/// Supported property traits:
77
/// Directed, Undirected, Unique, NoLoops, Reflexive, Weak, Unilateral,
8-
/// Connected, Subgraph
8+
/// Connected, Subgraph, Simple
99
///
1010
/// Warning: The Ensure implementation assumes the struct has 1 public member.
1111
/// If this is not the case, implement it yourself.
@@ -564,6 +564,22 @@ macro_rules! impl_properties {
564564
}
565565
}
566566
}
567+
568+
// Simple
569+
$crate::impl_properties!{
570+
@struct [ $struct ]
571+
@generic [ $($generics)* ]
572+
@delegate [ $delegate_type ]
573+
$(@exclude [ $($exclude_props)* ])?
574+
$(@include [ $($include_props)* ])?
575+
@bounds [
576+
<$delegate_type as $crate::core::GraphDeref>::Graph:
577+
$crate::core::property::Simple,
578+
$($bounds)*
579+
]
580+
@trait_id Simple [$crate::core::property]
581+
@implement {}
582+
}
567583
};
568584

569585
{

src/core/property/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ mod has_vertex;
88
mod no_loops;
99
mod reflexive;
1010
mod rooted;
11+
mod simple;
1112
mod subgraph;
1213
mod unilateral;
1314
mod unique;
1415
mod weak;
1516

1617
pub use self::{
1718
acyclic::*, base_props::*, connected::*, directedness_ensurers::*, has_vertex::*, no_loops::*,
18-
reflexive::*, rooted::*, subgraph::*, unilateral::*, unique::*, weak::*,
19+
reflexive::*, rooted::*, simple::*, subgraph::*, unilateral::*, unique::*, weak::*,
1920
};
2021
use crate::core::{
2122
proxy::{EdgeProxyGraph, ProxyVertex, VertexProxyGraph},

src/core/property/simple.rs

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use crate::core::{
2+
property::{AddEdge, NoLoops, NoLoopsGraph, Unique, UniqueGraph},
3+
Ensure, Graph, GraphDerefMut, Undirected,
4+
};
5+
use duplicate::duplicate_item;
6+
use std::borrow::Borrow;
7+
8+
/// A marker trait for [simple graphs](https://mathworld.wolfram.com/SimpleGraph.html)
9+
pub trait Simple: NoLoops + Unique {}
10+
11+
#[derive(Clone, Debug)]
12+
pub struct SimpleGraph<C: Ensure>(C);
13+
14+
impl<C: Ensure> SimpleGraph<C>
15+
where
16+
C::Graph: Graph<EdgeWeight = (), Directedness = Undirected>,
17+
{
18+
/// Constrains the given graph.
19+
///
20+
/// The given graph must be simple. This is not checked by this function.
21+
pub fn unchecked(c: C) -> Self
22+
{
23+
Self(c)
24+
}
25+
}
26+
27+
impl<C: Ensure> Ensure for SimpleGraph<C>
28+
where
29+
C::Graph: Graph<EdgeWeight = (), Directedness = Undirected>,
30+
{
31+
fn ensure_unvalidated(c: Self::Ensured, _: ()) -> Self
32+
{
33+
Self(c)
34+
}
35+
36+
fn validate(c: &Self::Ensured, _: &()) -> bool
37+
{
38+
NoLoopsGraph::<C>::validate(c, &()) && UniqueGraph::validate(c, &())
39+
}
40+
}
41+
42+
impl<C: Ensure + GraphDerefMut> AddEdge for SimpleGraph<C>
43+
where
44+
C::Graph: AddEdge<EdgeWeight = (), Directedness = Undirected>,
45+
{
46+
fn add_edge_weighted(
47+
&mut self,
48+
source: impl Borrow<Self::Vertex>,
49+
sink: impl Borrow<Self::Vertex>,
50+
weight: Self::EdgeWeight,
51+
) -> Result<(), ()>
52+
{
53+
if !(source.borrow() != sink.borrow()
54+
&& Self::can_add_edge(self, source.borrow(), sink.borrow()))
55+
{
56+
Err(())
57+
}
58+
else
59+
{
60+
self.0.graph_mut().add_edge_weighted(source, sink, weight)
61+
}
62+
}
63+
}
64+
65+
#[duplicate_item(
66+
Prop; [Unique]; [NoLoops]; [Simple];
67+
)]
68+
impl<C: Ensure> Prop for SimpleGraph<C> where
69+
C::Graph: Graph<EdgeWeight = (), Directedness = Undirected>
70+
{
71+
}
72+
73+
impl_ensurer! {
74+
use<C> SimpleGraph<C>: Ensure, Unique, NoLoops, Simple, AddEdge
75+
as (self.0) : C
76+
where C::Graph: Graph<EdgeWeight=(), Directedness=Undirected>
77+
}

src/core/property/unique.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ pub trait Unique: Graph
2121
{
2222
self.edges_between(v1, v2).next()
2323
}
24+
25+
/// Returns whether the given edge can be added to the graph without
26+
/// violating uniqueness
27+
fn can_add_edge<G: Graph>(
28+
graph: &G,
29+
source: impl Borrow<G::Vertex>,
30+
sink: impl Borrow<G::Vertex>,
31+
) -> bool
32+
{
33+
graph.edges_between(source, sink).next().is_none()
34+
}
2435
}
2536

2637
#[derive(Clone, Debug)]
@@ -76,10 +87,7 @@ where
7687
weight: Self::EdgeWeight,
7788
) -> Result<(), ()>
7889
{
79-
if self
80-
.edges_between(source.borrow(), sink.borrow())
81-
.next()
82-
.is_some()
90+
if !Self::can_add_edge(self, source.borrow(), sink.borrow())
8391
{
8492
return Err(());
8593
}
@@ -92,5 +100,4 @@ impl<C: Ensure> Unique for UniqueGraph<C> {}
92100
impl_ensurer! {
93101
use<C> UniqueGraph<C>: Ensure, Unique, AddEdge
94102
as (self.0) : C
95-
where C: Ensure
96103
}

tests/core/property/unique.rs

+48-39
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,65 @@ use crate::mock_graph::{
55
};
66
use duplicate::duplicate_item;
77
use graphene::core::{
8-
property::{AddEdge, HasVertex, NewVertex, UniqueGraph, VertexInGraph},
8+
property::{AddEdge, HasVertex, NewVertex, SimpleGraph, UniqueGraph, VertexInGraph},
99
Directed, EnsureUnloaded, Graph, ReleaseUnloaded, Undirected,
1010
};
1111

1212
#[duplicate_item(
13-
directedness; [ Directed ]; [ Undirected ]
13+
test_graph directedness_to_test edge_type;
14+
[ UniqueGraph ] [[Directed ]; [ Undirected ]] [MockEdgeWeight];
15+
[ SimpleGraph ] [[ Undirected ]] [()]
1416
)]
1517
mod __
1618
{
1719
use super::*;
18-
19-
/// Tests that UniqueGraph correctly identifies unique graphs.
20-
#[quickcheck]
21-
fn accept_unique(g: Arb<UniqueGraph<MockGraph<directedness>>>) -> bool
20+
#[duplicate_item(
21+
directedness; directedness_to_test
22+
)]
23+
mod __
2224
{
23-
UniqueGraph::validate(&g.0.release_all())
24-
}
25+
use super::*;
2526

26-
/// Tests that UniqueGraph correctly rejects non-unique graphs.
27-
#[quickcheck]
28-
fn reject_non_unique(g: Arb<NonUniqueGraph<directedness>>) -> bool
29-
{
30-
!UniqueGraph::validate(&g.0)
31-
}
27+
/// Tests that test_graph correctly identifies its own graphs.
28+
#[quickcheck]
29+
fn accept_property(g: Arb<test_graph<MockGraph<directedness, edge_type>>>) -> bool
30+
{
31+
test_graph::validate(&g.0.release_all())
32+
}
3233

33-
/// Tests that a UniqueGraph accepts adding a non-duplicate edge
34-
#[quickcheck]
35-
fn accept_add_edge(
36-
Arb(mut g): Arb<VertexInGraph<UniqueGraph<MockGraph<directedness>>>>,
37-
v_weight: MockVertexWeight,
38-
e_weight: MockEdgeWeight,
39-
) -> bool
40-
{
41-
let v = g.get_vertex().clone();
42-
// To ensure we add a non-duplicate edge,
43-
// we create a new vertex and add an edge to it from an existing one.
44-
let v2 = g.new_vertex_weighted(v_weight).unwrap();
45-
let accepted = g.add_edge_weighted(&v, &v2, e_weight).is_ok();
46-
accepted && g.edges_between(v, &v2).count() == 1
47-
}
34+
/// Tests that test_graph correctly rejects non-unique graphs.
35+
#[quickcheck]
36+
fn reject_non_unique(g: Arb<NonUniqueGraph<directedness, edge_type>>) -> bool
37+
{
38+
!test_graph::validate(&g.0)
39+
}
4840

49-
/// Tests that a UniqueGraph rejects adding a duplicate edge
50-
#[quickcheck]
51-
fn reject_add_edge(
52-
Arb(g): Arb<EdgeIn<UniqueGraph<MockGraph<directedness>>>>,
53-
weight: MockEdgeWeight,
54-
) -> bool
55-
{
56-
let source = g.get_vertex();
57-
let EdgeIn(mut g, sink, _) = g;
58-
g.add_edge_weighted(source, sink, weight).is_err()
41+
/// Tests that a test_graph accepts adding a non-duplicate edge
42+
#[quickcheck]
43+
fn accept_add_edge(
44+
Arb(mut g): Arb<VertexInGraph<test_graph<MockGraph<directedness, edge_type>>>>,
45+
v_weight: MockVertexWeight,
46+
e_weight: edge_type,
47+
) -> bool
48+
{
49+
let v = g.get_vertex().clone();
50+
// To ensure we add a non-duplicate edge,
51+
// we create a new vertex and add an edge to it from an existing one.
52+
let v2 = g.new_vertex_weighted(v_weight).unwrap();
53+
let accepted = g.add_edge_weighted(&v, &v2, e_weight).is_ok();
54+
accepted && g.edges_between(v, &v2).count() == 1
55+
}
56+
57+
/// Tests that a test_graph rejects adding a duplicate edge
58+
#[quickcheck]
59+
fn reject_add_edge(
60+
Arb(g): Arb<EdgeIn<test_graph<MockGraph<directedness, edge_type>>>>,
61+
weight: edge_type,
62+
) -> bool
63+
{
64+
let source = g.get_vertex();
65+
let EdgeIn(mut g, sink, _) = g;
66+
g.add_edge_weighted(source, sink, weight).is_err()
67+
}
5968
}
6069
}

tests/mock_graph/arbitrary/combinations/edge_in.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::mock_graph::{
22
arbitrary::{GuidedArbGraph, Limit},
3-
MockEdgeWeight, MockVertex, TestGraph,
3+
MockType, MockVertex, TestGraph,
44
};
55
use graphene::{
66
core::{
@@ -11,21 +11,27 @@ use graphene::{
1111
};
1212
use quickcheck::{Arbitrary, Gen};
1313
use rand::Rng;
14-
use std::collections::HashSet;
14+
use std::{collections::HashSet, fmt::Debug};
1515

1616
/// An arbitrary graph with an edge that is guaranteed to be in the graph (the
1717
/// weight is a clone).
1818
/// The source of the edge can be accessed through `.get_vertex`, the sink `.1`,
1919
/// and the weight `.2`
2020
#[derive(Clone, Debug)]
21-
pub struct EdgeIn<G: GuidedArbGraph>(pub VertexInGraph<G>, pub MockVertex, pub MockEdgeWeight)
21+
pub struct EdgeIn<G: GuidedArbGraph>(
22+
pub VertexInGraph<G>,
23+
pub MockVertex,
24+
pub <G::Graph as Graph>::EdgeWeight,
25+
)
2226
where
23-
G::Graph: TestGraph;
27+
G::Graph: TestGraph,
28+
<G::Graph as Graph>::EdgeWeight: MockType;
2429

2530
impl<G> graphene::core::Ensure for EdgeIn<G>
2631
where
2732
G: GuidedArbGraph,
2833
G::Graph: TestGraph,
34+
<G::Graph as Graph>::EdgeWeight: MockType,
2935
{
3036
fn ensure_unvalidated(c: Self::Ensured, _: ()) -> Self
3137
{
@@ -46,13 +52,15 @@ impl_ensurer! {
4652
as ( self.0) : VertexInGraph<G>
4753
where
4854
G: GuidedArbGraph,
49-
G::Graph: TestGraph
55+
G::Graph: TestGraph,
56+
<G::Graph as Graph>::EdgeWeight: MockType
5057
}
5158

5259
impl<Gr> GuidedArbGraph for EdgeIn<Gr>
5360
where
5461
Gr: GuidedArbGraph + GraphDerefMut,
5562
Gr::Graph: TestGraph + GraphMut + AddEdge + RemoveEdge,
63+
<Gr::Graph as Graph>::EdgeWeight: MockType,
5664
{
5765
fn choose_size<G: Gen>(
5866
g: &mut G,
@@ -119,15 +127,15 @@ where
119127
for w in self.edges_between(v1, v2)
120128
{
121129
// Remove edge
122-
if !saw_reference_edge_before && w.value == self.2.value
130+
if !saw_reference_edge_before && *w == self.2
123131
{
124132
// Cannot remove the reference edge, if its the only one
125133
saw_reference_edge_before = true
126134
}
127135
else
128136
{
129137
let mut g = self.clone();
130-
g.remove_edge_where_weight(v1, v2, |ref weight| w.value == weight.value)
138+
g.remove_edge_where_weight(v1, v2, |ref weight| w == *weight)
131139
.unwrap();
132140
result.push(g);
133141
}
@@ -137,9 +145,9 @@ where
137145
let mut shrunk_graph = self.clone();
138146
*shrunk_graph
139147
.edges_between_mut(v1, v2)
140-
.find(|w| w.value == self.2.value)
148+
.find(|w| **w == self.2)
141149
.unwrap() = s_w.clone();
142-
if !shrunk_reference_weight_before && self.2.value == w.value
150+
if !shrunk_reference_weight_before && self.2 == *w
143151
{
144152
shrunk_graph.2 = s_w;
145153
// We only need to update the reference weight for one edge

0 commit comments

Comments
 (0)