Skip to content

Commit 7e7ebfb

Browse files
committed
Merge branch 'release/0.9.0'
2 parents fb3864d + d3c1d7b commit 7e7ebfb

5 files changed

Lines changed: 200 additions & 16 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ Contributions are welcome. Please submit a pull request to the develop branch in
9191

9292
# Releases
9393

94+
## 0.9.0
95+
96+
Added reversePostOrder to DirectedGraphPlugin. This will allow a DirectedGraph to perform a
97+
closure on each vertex in reverse post order (topological order). Also added reversePostOrderSort which
98+
returns the vertex names in reverse post order of a graph.
99+
100+
DirectedGraphPlugin was reorganized making it possible to javadoc methods dynamically added to the graph.
101+
The methods added are static in DirectedGraphPlugin. They are added to Graph's metaClass using method pointers
102+
while currying the graph param to the apply method. This could be used in future plugins.
103+
94104
## 0.8.2
95105

96106
* fixing publishing of javadoc and groovydoc. gh-pages branch must exist.

src/main/groovy/graph/DirectedGraphPlugin.groovy

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ class DirectedGraphPlugin implements Plugin {
77

88
/**
99
* The following modifications are made to the graph:
10-
*
10+
* <p>
1111
* members
1212
* edges - converted to DirectedEdges
1313
* edgeFactory - changed to a DirectedEdgeFactory
14-
*
14+
* <p>
1515
* methods
16-
* outEdges - returns out going edges from a given vertex name
17-
* adjacentEdges - now only returns outgoing edges from the given vertex name
16+
* The static graph methods in this class are added to the graph with graph as the first parameter.
1817
* @param graph
1918
* @return
2019
*/
@@ -25,14 +24,93 @@ class DirectedGraphPlugin implements Plugin {
2524

2625
graph.edgeFactory = new DirectedEdgeFactory()
2726

28-
graph.metaClass.outEdges = { name ->
29-
graph.@edges.findAll {
30-
name == it.one
27+
graph.metaClass.inEdges = this.&inEdges.curry(graph)
28+
graph.metaClass.inDegree = this.&inDegree.curry(graph)
29+
graph.metaClass.outEdges = this.&outEdges.curry(graph)
30+
graph.metaClass.outDegree = this.&outDegree.curry(graph)
31+
graph.metaClass.adjacentEdges = this.&adjacentEdges.curry(graph)
32+
graph.metaClass.reversePostOrderSort = this.&reversePostOrderSort.curry(graph)
33+
graph.metaClass.reversePostOrder = this.&reversePostOrder.curry(graph)
34+
}
35+
36+
/**
37+
* Returns in edges from vertex with name in graph.
38+
* @param graph
39+
* @param name
40+
* @return
41+
*/
42+
static Set<? extends Edge> inEdges(Graph graph, String name) {
43+
graph.@edges.findAll {
44+
name == it.two
45+
}
46+
}
47+
48+
/**
49+
* returns number of in edges from vertex with name in graph.
50+
* @param graph
51+
* @param name
52+
* @return
53+
*/
54+
static int inDegree(Graph graph, String name) {
55+
graph.inEdges(name).size()
56+
}
57+
58+
/**
59+
* returns out edges with vertex name in graph.
60+
* @param graph
61+
* @param name
62+
* @return
63+
*/
64+
static Set<? extends Edge> outEdges(Graph graph, String name) {
65+
graph.@edges.findAll {
66+
name == it.one
67+
}
68+
}
69+
70+
/**
71+
* returns number of out edges with vertex name in graph.
72+
* @param graph
73+
* @param name
74+
* @return
75+
*/
76+
static int outDegree(Graph graph, String name) {
77+
graph.outEdges(name).size()
78+
}
79+
80+
/**
81+
* returns out edges with vertex name in graph. This method replaces adjacentEdges in {@link Graph}.
82+
* @param graph
83+
* @param name
84+
* @return
85+
*/
86+
static Set<? extends Edge> adjacentEdges(Graph graph, String name) {
87+
graph.outEdges(name)
88+
}
89+
90+
/**
91+
* Returns the names of vertices sorted in reverse post order for the given graph.
92+
* @param graph
93+
* @return
94+
*/
95+
static Deque<String> reversePostOrderSort(Graph graph) {
96+
Deque<String> deque = new LinkedList<String>()
97+
graph.depthFirstTraversal {
98+
postorder { vertex ->
99+
deque.addFirst(vertex.name)
31100
}
32101
}
102+
deque
103+
}
33104

34-
graph.metaClass.adjacentEdges = { name ->
35-
outEdges name
105+
/**
106+
* Runs the given closure on each vertex in graph in reverse post order.
107+
* @param graph
108+
* @param closure
109+
*/
110+
static void reversePostOrder(Graph graph, Closure closure) {
111+
Deque<String> deque = graph.reversePostOrderSort()
112+
deque.each {
113+
closure(graph.@vertices[it])
36114
}
37115
}
38116
}

src/main/groovy/graph/Graph.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ class Graph {
403403
* @param name
404404
* @return set of adjacent edges.
405405
*/
406-
Set<Edge> adjacentEdges(name) {
406+
Set<? extends Edge> adjacentEdges(name) {
407407
edges.findAll {
408408
name == it.one || name == it.two
409409
}

src/test/groovy/graph/DirectedGraphSpec.groovy

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,47 @@ class DirectedGraphSpec extends Specification {
99
graph.apply DirectedGraphPlugin
1010
}
1111

12-
def 'can get outedges and adjacent edges'() {
12+
def 'can get out edges'() {
1313
setup:
14-
def expected = [] as LinkedHashSet
14+
def expected = [] as LinkedHashSet<DirectedEdge>
15+
graph.with {
16+
expected << edge('step1', 'step2')
17+
expected << edge('step1', 'step3')
18+
edge 'step4', 'step1'
19+
edge 'step4', 'step3'
20+
}
21+
22+
when:
23+
def out = graph.outEdges 'step1'
24+
25+
then:
26+
out == expected
27+
}
28+
29+
def 'can get out degree'() {
30+
setup:
31+
graph.with {
32+
edge('step1', 'step2')
33+
edge('step1', 'step3')
34+
}
35+
36+
when:
37+
def out = graph.outDegree 'step1'
38+
39+
then:
40+
out == 2
41+
}
42+
43+
def 'adjacentEdges are outEdges'() {
44+
setup:
45+
def expected = [] as LinkedHashSet<DirectedEdge>
1546
graph.with {
16-
vertex 'step1'
1747
expected << edge('step1', 'step2')
1848
expected << edge('step1', 'step3')
1949
edge 'step4', 'step1'
2050
edge 'step4', 'step3'
2151
}
52+
2253
when:
2354
def out = graph.outEdges 'step1'
2455
def adjacent = graph.adjacentEdges 'step1'
@@ -28,6 +59,37 @@ class DirectedGraphSpec extends Specification {
2859
adjacent == expected
2960
}
3061

62+
def 'can get inEdges'() {
63+
setup:
64+
def expected = [] as LinkedHashSet<DirectedEdge>
65+
graph.with {
66+
expected << edge('step2', 'step1')
67+
expected << edge('step3', 'step1')
68+
edge 'step1', 'step4'
69+
edge 'step3', 'step4'
70+
}
71+
72+
when:
73+
def inEdges = graph.inEdges 'step1'
74+
75+
then:
76+
inEdges == expected
77+
}
78+
79+
def 'can get in degree'() {
80+
setup:
81+
graph.with {
82+
edge('step2', 'step1')
83+
edge('step3', 'step1')
84+
}
85+
86+
when:
87+
def inEdges = graph.inDegree 'step1'
88+
89+
then:
90+
inEdges == 2
91+
}
92+
3193
def 'traverse is directed'() {
3294
setup:
3395
graph.with {
@@ -62,4 +124,41 @@ class DirectedGraphSpec extends Specification {
62124
preorderList == ['step1', 'step3', 'step5', 'step2', 'step4']
63125
postorderList == ['step2', 'step5', 'step3', 'step4', 'step1']
64126
}
127+
128+
def 'can get topological sort'() {
129+
setup:
130+
graph.with {
131+
vertex('A', [connectsTo:['B', 'D', 'E']])
132+
vertex('B', [connectsTo:['D', 'C']])
133+
vertex('C', [connectsTo:'D'])
134+
vertex('D', [connectsTo:'G'])
135+
vertex('G', [connectsTo:'F'])
136+
}
137+
138+
when:
139+
def sort = graph.reversePostOrderSort()
140+
141+
then:
142+
sort == ['A', 'E', 'B', 'C', 'D', 'G', 'F']
143+
}
144+
145+
def 'can perform reversePostOrder'() {
146+
setup:
147+
graph.with {
148+
vertex('A', [connectsTo:['B', 'D', 'E']])
149+
vertex('B', [connectsTo:['D', 'C']])
150+
vertex('C', [connectsTo:'D'])
151+
vertex('D', [connectsTo:'G'])
152+
vertex('G', [connectsTo:'F'])
153+
}
154+
def rpo = []
155+
156+
when:
157+
graph.reversePostOrder { vertex ->
158+
rpo << vertex.name
159+
}
160+
161+
then:
162+
rpo == ['A', 'E', 'B', 'C', 'D', 'G', 'F']
163+
}
65164
}

travis.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; th
3333
cp -Rf build/docs/groovydoc .
3434
git add -f .
3535
git commit -m "Lastest groovydoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
36-
git rm -rf javadoc
37-
git add -f .
38-
git commit -m "Lastest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
3936
git push -fq origin gh-pages
4037
cd ..
4138

0 commit comments

Comments
 (0)