Skip to content

Commit c9629cb

Browse files
committed
Adding reverse post order
1 parent 243245a commit c9629cb

7 files changed

Lines changed: 344 additions & 130 deletions

File tree

graphs-core/src/main/java/com/github/moaxcp/graphs/PropertyGraph.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,49 @@ default Stream<Vertex<ID>> breadthFirstStream(ID... start) {
971971
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
972972
}
973973

974+
/**
975+
* Returns a reverse-post-order {@link Iterator} of every {@link Vertex} in this graph. This is a reverse of a
976+
* postOrderIterator starting at the provided vertices.
977+
* @throws NullPointerException if start is null or any id in start
978+
* @throws IllegalArgumentException if start contains ids that are not in the graph
979+
* @param start ids of traversal
980+
* @return
981+
*/
982+
default Iterator<Vertex<ID>> reversePostOrderIterator(ID... start) {
983+
var iterator = postOrderIterator(start);
984+
var list = new LinkedList<Vertex<ID>>();
985+
while(iterator.hasNext()) {
986+
list.addFirst(iterator.next());
987+
}
988+
return new Iterator<Vertex<ID>>() {
989+
@Override
990+
public boolean hasNext() {
991+
return list.size() != 0;
992+
}
993+
994+
@Override
995+
public Vertex<ID> next() {
996+
if(list.size() == 0) {
997+
throw new NoSuchElementException("Could not find next element.");
998+
}
999+
return list.removeFirst();
1000+
}
1001+
};
1002+
}
1003+
1004+
/**
1005+
* Returns a reverse-post-order {@link Stream} of every {@link Vertex} in this graph. This is a reverse of a
1006+
* postOrderIterator starting at the provided vertices.
1007+
* @throws NullPointerException if start is null or any id in start
1008+
* @throws IllegalArgumentException if start contains ids that are not in the graph
1009+
* @param start ids of traversal
1010+
* @return
1011+
*/
1012+
default Stream<Vertex<ID>> reversePostOrderStream(ID... start) {
1013+
var iterator = reversePostOrderIterator(start);
1014+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
1015+
}
1016+
9741017
/**
9751018
* Returns true if this graph is empty.
9761019
* @return

graphs-manual/src/docs/asciidoc/index.adoc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,24 @@ include::{sourcedir}/com/github/moaxcp/graphs/manual/PostorderTraversal.java[tag
345345

346346
image::images/postOrderTraversal.gif[post order]
347347

348+
== reverse-post-order iterator and post-order stream
349+
350+
An iterator and stream are provided to perform reverse-post-order depth first traversal.
351+
352+
.reversePostOrderIterator()
353+
[source,java,indent=0]
354+
----
355+
include::{sourcedir}/com/github/moaxcp/graphs/manual/ReversePostorderTraversal.java[tags=reversePostOrderIterator]
356+
----
357+
358+
.reversePostOrderStream()
359+
[source,java,indent=0]
360+
----
361+
include::{sourcedir}/com/github/moaxcp/graphs/manual/ReversePostorderTraversal.java[tags=reversePostOrderStream]
362+
----
363+
364+
image::images/reversePostOrderTraversal.gif[reverse post order]
365+
348366
== breadth first iterator and breadth first stream
349367

350368
An iterator and stream are provided to perform breadth first traversals.
@@ -365,6 +383,10 @@ image::images/breadthFirstTraversal.gif[post order]
365383

366384
== Releases
367385

386+
== 0.16.0-SNAPSHOT
387+
388+
Adding reversePostOrder iterator and stream.
389+
368390
== 0.15.0
369391

370392
Adding new project `graphs-guava` and `graphs-graphviz-guava-gif`.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.github.moaxcp.graphs.manual;
2+
3+
import com.github.moaxcp.graphs.DirectedPropertyGraph;
4+
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
5+
import com.github.moaxcp.graphs.graphviz.greenrobotgif.GreenrobotGifSubscriber;
6+
import com.github.moaxcp.graphs.greenrobot.DirectedEventPropertyGraph;
7+
import java.nio.file.Path;
8+
import java.util.Iterator;
9+
import org.greenrobot.eventbus.EventBus;
10+
import org.junit.jupiter.api.Test;
11+
12+
public class ReversePostorderTraversal {
13+
14+
@Test
15+
void reversePostOrderTraversal() {
16+
var bus = new EventBus();
17+
var graph = new DirectedEventPropertyGraph<String>(bus);
18+
19+
graph.edge("A", "B")
20+
.edge("B", "C")
21+
.edge("B", "D")
22+
.edge("D", "E")
23+
.edge("D", "C")
24+
.edge("A", "D")
25+
.edge("D", "A")
26+
.edge("A", "E")
27+
.edge("F", "G")
28+
.edge("G", "D");
29+
var gif = new GreenrobotGifSubscriber<>(graph, Path.of("src/docs/asciidoc/images/reversePostOrderTraversal.gif"));
30+
31+
// tag::reversePostOrderIterator[]
32+
Iterator<Vertex<String>> iterator = graph.reversePostOrderIterator("A");
33+
while(iterator.hasNext()) {
34+
Vertex<String> vertex = iterator.next();
35+
vertex.property("color", "green");
36+
}
37+
// end::reversePostOrderIterator[]
38+
gif.writeFile();
39+
}
40+
41+
@Test
42+
void postOrderStream() {
43+
var graph = new DirectedPropertyGraph<String>();
44+
45+
graph.edge("A", "B")
46+
.edge("B", "C")
47+
.edge("B", "D")
48+
.edge("D", "E")
49+
.edge("D", "C")
50+
.edge("A", "D")
51+
.edge("D", "A")
52+
.edge("A", "E")
53+
.edge("F", "G")
54+
.edge("G", "D");
55+
// tag::reversePostOrderStream[]
56+
graph.reversePostOrderStream("A").forEach(v -> v.property("color", "green"));
57+
// end::reversePostOrderStream[]
58+
}
59+
}

graphs-test/src/test/java/publicapi/PostOrderDepthFirstIteratorTest.java

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
package publicapi;
22

3-
import com.github.moaxcp.graphs.*;
4-
import com.github.moaxcp.graphs.PropertyGraph.*;
5-
import com.github.moaxcp.graphs.testframework.*;
6-
import org.junit.jupiter.api.*;
7-
import org.junit.jupiter.params.*;
8-
import org.junit.jupiter.params.provider.*;
9-
10-
import java.util.*;
11-
12-
import static com.github.moaxcp.graphs.testframework.MethodSources.*;
13-
import static com.github.moaxcp.graphs.testframework.PathOrder.*;
14-
import static com.google.common.truth.Truth.*;
15-
import static java.util.stream.Collectors.*;
16-
import static org.junit.jupiter.api.Assertions.*;
3+
import com.github.moaxcp.graphs.PropertyGraph;
4+
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
5+
import com.github.moaxcp.graphs.testframework.TestGraphs;
6+
import java.util.List;
7+
import java.util.NoSuchElementException;
8+
import org.junit.jupiter.api.DisplayName;
9+
import org.junit.jupiter.params.ParameterizedTest;
10+
import org.junit.jupiter.params.provider.MethodSource;
11+
12+
import static com.google.common.truth.Truth.assertThat;
13+
import static java.util.stream.Collectors.toList;
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
1715

1816
public class PostOrderDepthFirstIteratorTest {
1917

@@ -98,18 +96,6 @@ void postOrderIterator(String name, PropertyGraph<String> graph, String[] start,
9896
}
9997
}
10098

101-
@TestDirectedGraphs
102-
void postOrderIteratorStart(PropertyGraph<String> graph) {
103-
complexTwoComponents(graph, POST_ORDER);
104-
var result = new ArrayList<String>();
105-
var iterator = graph.postOrderIterator("D", "G", "W");
106-
while(iterator.hasNext()) {
107-
result.add(iterator.next().getId());
108-
}
109-
110-
assertThat(result).isEqualTo(List.of("E", "C", "B", "A", "D", "G", "W", "F", "Y", "Z", "X"));
111-
}
112-
11399
@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsPostOrder")
114100
@DisplayName("postOrderStream matches expected order")
115101
@ParameterizedTest(name = "{index} - {0} {2}")
@@ -120,14 +106,4 @@ void postOrderStream(String name, PropertyGraph<String> graph, String[] start, L
120106

121107
assertThat(result).isEqualTo(expectedOrder);
122108
}
123-
124-
@TestDirectedGraphs
125-
void postOrderStreamStart(PropertyGraph<String> graph) {
126-
complexTwoComponents(graph, POST_ORDER);
127-
var result = graph.postOrderStream("D", "G", "W")
128-
.map(Vertex::getId)
129-
.collect(toList());
130-
131-
assertThat(result).isEqualTo(List.of("E", "C", "B", "A", "D", "G", "W", "F", "Y", "Z", "X"));
132-
}
133109
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package publicapi;
2+
3+
import com.github.moaxcp.graphs.PropertyGraph;
4+
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
5+
import com.github.moaxcp.graphs.testframework.TestGraphs;
6+
import java.util.List;
7+
import java.util.NoSuchElementException;
8+
import org.junit.jupiter.api.DisplayName;
9+
import org.junit.jupiter.params.ParameterizedTest;
10+
import org.junit.jupiter.params.provider.MethodSource;
11+
12+
import static com.google.common.truth.Truth.assertThat;
13+
import static java.util.stream.Collectors.toList;
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
15+
16+
public class ReversePostOrderDepthFirstIteratorTest {
17+
18+
@TestGraphs
19+
void nullStart(PropertyGraph<String> graph) {
20+
var exception = assertThrows(NullPointerException.class, () -> graph.reversePostOrderIterator((String[]) null));
21+
assertThat(exception).hasMessageThat().isEqualTo("start is marked non-null but is null");
22+
}
23+
24+
@TestGraphs
25+
void nullInOther(PropertyGraph<String> graph) {
26+
graph.vertex("A").vertex("B");
27+
var exception = assertThrows(NullPointerException.class, () -> graph.reversePostOrderIterator("A", null, "B"));
28+
assertThat(exception).hasMessageThat().isEqualTo("\"id\" in \"start\" must not be null.");
29+
}
30+
31+
@TestGraphs
32+
void startNotInGraph(PropertyGraph<String> graph) {
33+
var exception = assertThrows(IllegalArgumentException.class, () -> graph.reversePostOrderIterator("A"));
34+
assertThat(exception).hasMessageThat().isEqualTo("vertex \"A\" not found in graph.");
35+
}
36+
37+
@TestGraphs
38+
void hasNext_EmptyGraph(PropertyGraph<String> graph) {
39+
var iterator = graph.reversePostOrderIterator();
40+
assertThat(iterator.hasNext()).isFalse();
41+
}
42+
43+
@TestGraphs
44+
void next_EmptyGraph(PropertyGraph<String> graph) {
45+
var iterator = graph.reversePostOrderIterator();
46+
var exception = assertThrows(NoSuchElementException.class, () -> iterator.next());
47+
assertThat(exception).hasMessageThat().isEqualTo("Could not find next element.");
48+
}
49+
50+
@TestGraphs
51+
void hasNext_beforeIteration(PropertyGraph<String> graph) {
52+
graph.vertex("A");
53+
var iterator = graph.reversePostOrderIterator();
54+
assertThat(iterator.hasNext()).isTrue();
55+
}
56+
57+
@TestGraphs
58+
void hasNext_MultipleBeforeIteration(PropertyGraph<String> graph) {
59+
graph.vertex("A");
60+
var iterator = graph.reversePostOrderIterator();
61+
assertThat(iterator.hasNext()).isTrue();
62+
assertThat(iterator.hasNext()).isTrue();
63+
assertThat(iterator.hasNext()).isTrue();
64+
assertThat(iterator.next().getId()).isEqualTo("A");
65+
}
66+
67+
@TestGraphs
68+
void hasNext_MultipleBetweenComponents(PropertyGraph<String> graph) {
69+
graph.vertex("A");
70+
graph.vertex("B");
71+
var iterator = graph.reversePostOrderIterator();
72+
assertThat(iterator.hasNext()).isTrue();
73+
assertThat(iterator.next().getId()).isEqualTo("B");
74+
assertThat(iterator.hasNext()).isTrue();
75+
assertThat(iterator.hasNext()).isTrue();
76+
assertThat(iterator.hasNext()).isTrue();
77+
assertThat(iterator.next().getId()).isEqualTo("A");
78+
}
79+
80+
@TestGraphs
81+
void next_withoutCallingHasNext(PropertyGraph<String> graph) {
82+
graph.vertex("A");
83+
var iterator = graph.reversePostOrderIterator();
84+
assertThat(iterator.next().getId()).isEqualTo("A");
85+
assertThat(iterator.hasNext()).isFalse();
86+
}
87+
88+
@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsReversePostOrder")
89+
@DisplayName("reversePostOrderIterator matches expected order")
90+
@ParameterizedTest(name = "{index} - {0} {2}")
91+
void reversePostOrderIterator(String name, PropertyGraph<String> graph, String[] start, List<String> expectedOrder) {
92+
var iterator = graph.reversePostOrderIterator(start);
93+
for(String expected : expectedOrder) {
94+
String result = iterator.next().getId();
95+
assertThat(result).isEqualTo(expected);
96+
}
97+
}
98+
99+
@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsReversePostOrder")
100+
@DisplayName("reversePostOrderStream matches expected order")
101+
@ParameterizedTest(name = "{index} - {0} {2}")
102+
void reversePostOrderStream(String name, PropertyGraph<String> graph, String[] start, List<String> expectedOrder) {
103+
var result = graph.reversePostOrderStream(start)
104+
.map(Vertex::getId)
105+
.collect(toList());
106+
107+
assertThat(result).isEqualTo(expectedOrder);
108+
}
109+
}

0 commit comments

Comments
 (0)