Skip to content

Commit

Permalink
Adding reverse post order
Browse files Browse the repository at this point in the history
  • Loading branch information
moaxcp committed Jul 5, 2022
1 parent 243245a commit c9629cb
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,49 @@ default Stream<Vertex<ID>> breadthFirstStream(ID... start) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
}

/**
* Returns a reverse-post-order {@link Iterator} of every {@link Vertex} in this graph. This is a reverse of a
* postOrderIterator starting at the provided vertices.
* @throws NullPointerException if start is null or any id in start
* @throws IllegalArgumentException if start contains ids that are not in the graph
* @param start ids of traversal
* @return
*/
default Iterator<Vertex<ID>> reversePostOrderIterator(ID... start) {
var iterator = postOrderIterator(start);
var list = new LinkedList<Vertex<ID>>();
while(iterator.hasNext()) {
list.addFirst(iterator.next());
}
return new Iterator<Vertex<ID>>() {
@Override
public boolean hasNext() {
return list.size() != 0;
}

@Override
public Vertex<ID> next() {
if(list.size() == 0) {
throw new NoSuchElementException("Could not find next element.");
}
return list.removeFirst();
}
};
}

/**
* Returns a reverse-post-order {@link Stream} of every {@link Vertex} in this graph. This is a reverse of a
* postOrderIterator starting at the provided vertices.
* @throws NullPointerException if start is null or any id in start
* @throws IllegalArgumentException if start contains ids that are not in the graph
* @param start ids of traversal
* @return
*/
default Stream<Vertex<ID>> reversePostOrderStream(ID... start) {
var iterator = reversePostOrderIterator(start);
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
}

/**
* Returns true if this graph is empty.
* @return
Expand Down
22 changes: 22 additions & 0 deletions graphs-manual/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,24 @@ include::{sourcedir}/com/github/moaxcp/graphs/manual/PostorderTraversal.java[tag

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

== reverse-post-order iterator and post-order stream

An iterator and stream are provided to perform reverse-post-order depth first traversal.

.reversePostOrderIterator()
[source,java,indent=0]
----
include::{sourcedir}/com/github/moaxcp/graphs/manual/ReversePostorderTraversal.java[tags=reversePostOrderIterator]
----

.reversePostOrderStream()
[source,java,indent=0]
----
include::{sourcedir}/com/github/moaxcp/graphs/manual/ReversePostorderTraversal.java[tags=reversePostOrderStream]
----

image::images/reversePostOrderTraversal.gif[reverse post order]

== breadth first iterator and breadth first stream

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

== Releases

== 0.16.0-SNAPSHOT

Adding reversePostOrder iterator and stream.

== 0.15.0

Adding new project `graphs-guava` and `graphs-graphviz-guava-gif`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.github.moaxcp.graphs.manual;

import com.github.moaxcp.graphs.DirectedPropertyGraph;
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
import com.github.moaxcp.graphs.graphviz.greenrobotgif.GreenrobotGifSubscriber;
import com.github.moaxcp.graphs.greenrobot.DirectedEventPropertyGraph;
import java.nio.file.Path;
import java.util.Iterator;
import org.greenrobot.eventbus.EventBus;
import org.junit.jupiter.api.Test;

public class ReversePostorderTraversal {

@Test
void reversePostOrderTraversal() {
var bus = new EventBus();
var graph = new DirectedEventPropertyGraph<String>(bus);

graph.edge("A", "B")
.edge("B", "C")
.edge("B", "D")
.edge("D", "E")
.edge("D", "C")
.edge("A", "D")
.edge("D", "A")
.edge("A", "E")
.edge("F", "G")
.edge("G", "D");
var gif = new GreenrobotGifSubscriber<>(graph, Path.of("src/docs/asciidoc/images/reversePostOrderTraversal.gif"));

// tag::reversePostOrderIterator[]
Iterator<Vertex<String>> iterator = graph.reversePostOrderIterator("A");
while(iterator.hasNext()) {
Vertex<String> vertex = iterator.next();
vertex.property("color", "green");
}
// end::reversePostOrderIterator[]
gif.writeFile();
}

@Test
void postOrderStream() {
var graph = new DirectedPropertyGraph<String>();

graph.edge("A", "B")
.edge("B", "C")
.edge("B", "D")
.edge("D", "E")
.edge("D", "C")
.edge("A", "D")
.edge("D", "A")
.edge("A", "E")
.edge("F", "G")
.edge("G", "D");
// tag::reversePostOrderStream[]
graph.reversePostOrderStream("A").forEach(v -> v.property("color", "green"));
// end::reversePostOrderStream[]
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package publicapi;

import com.github.moaxcp.graphs.*;
import com.github.moaxcp.graphs.PropertyGraph.*;
import com.github.moaxcp.graphs.testframework.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.*;
import org.junit.jupiter.params.provider.*;

import java.util.*;

import static com.github.moaxcp.graphs.testframework.MethodSources.*;
import static com.github.moaxcp.graphs.testframework.PathOrder.*;
import static com.google.common.truth.Truth.*;
import static java.util.stream.Collectors.*;
import static org.junit.jupiter.api.Assertions.*;
import com.github.moaxcp.graphs.PropertyGraph;
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
import com.github.moaxcp.graphs.testframework.TestGraphs;
import java.util.List;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class PostOrderDepthFirstIteratorTest {

Expand Down Expand Up @@ -98,18 +96,6 @@ void postOrderIterator(String name, PropertyGraph<String> graph, String[] start,
}
}

@TestDirectedGraphs
void postOrderIteratorStart(PropertyGraph<String> graph) {
complexTwoComponents(graph, POST_ORDER);
var result = new ArrayList<String>();
var iterator = graph.postOrderIterator("D", "G", "W");
while(iterator.hasNext()) {
result.add(iterator.next().getId());
}

assertThat(result).isEqualTo(List.of("E", "C", "B", "A", "D", "G", "W", "F", "Y", "Z", "X"));
}

@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsPostOrder")
@DisplayName("postOrderStream matches expected order")
@ParameterizedTest(name = "{index} - {0} {2}")
Expand All @@ -120,14 +106,4 @@ void postOrderStream(String name, PropertyGraph<String> graph, String[] start, L

assertThat(result).isEqualTo(expectedOrder);
}

@TestDirectedGraphs
void postOrderStreamStart(PropertyGraph<String> graph) {
complexTwoComponents(graph, POST_ORDER);
var result = graph.postOrderStream("D", "G", "W")
.map(Vertex::getId)
.collect(toList());

assertThat(result).isEqualTo(List.of("E", "C", "B", "A", "D", "G", "W", "F", "Y", "Z", "X"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package publicapi;

import com.github.moaxcp.graphs.PropertyGraph;
import com.github.moaxcp.graphs.PropertyGraph.Vertex;
import com.github.moaxcp.graphs.testframework.TestGraphs;
import java.util.List;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class ReversePostOrderDepthFirstIteratorTest {

@TestGraphs
void nullStart(PropertyGraph<String> graph) {
var exception = assertThrows(NullPointerException.class, () -> graph.reversePostOrderIterator((String[]) null));
assertThat(exception).hasMessageThat().isEqualTo("start is marked non-null but is null");
}

@TestGraphs
void nullInOther(PropertyGraph<String> graph) {
graph.vertex("A").vertex("B");
var exception = assertThrows(NullPointerException.class, () -> graph.reversePostOrderIterator("A", null, "B"));
assertThat(exception).hasMessageThat().isEqualTo("\"id\" in \"start\" must not be null.");
}

@TestGraphs
void startNotInGraph(PropertyGraph<String> graph) {
var exception = assertThrows(IllegalArgumentException.class, () -> graph.reversePostOrderIterator("A"));
assertThat(exception).hasMessageThat().isEqualTo("vertex \"A\" not found in graph.");
}

@TestGraphs
void hasNext_EmptyGraph(PropertyGraph<String> graph) {
var iterator = graph.reversePostOrderIterator();
assertThat(iterator.hasNext()).isFalse();
}

@TestGraphs
void next_EmptyGraph(PropertyGraph<String> graph) {
var iterator = graph.reversePostOrderIterator();
var exception = assertThrows(NoSuchElementException.class, () -> iterator.next());
assertThat(exception).hasMessageThat().isEqualTo("Could not find next element.");
}

@TestGraphs
void hasNext_beforeIteration(PropertyGraph<String> graph) {
graph.vertex("A");
var iterator = graph.reversePostOrderIterator();
assertThat(iterator.hasNext()).isTrue();
}

@TestGraphs
void hasNext_MultipleBeforeIteration(PropertyGraph<String> graph) {
graph.vertex("A");
var iterator = graph.reversePostOrderIterator();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next().getId()).isEqualTo("A");
}

@TestGraphs
void hasNext_MultipleBetweenComponents(PropertyGraph<String> graph) {
graph.vertex("A");
graph.vertex("B");
var iterator = graph.reversePostOrderIterator();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next().getId()).isEqualTo("B");
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next().getId()).isEqualTo("A");
}

@TestGraphs
void next_withoutCallingHasNext(PropertyGraph<String> graph) {
graph.vertex("A");
var iterator = graph.reversePostOrderIterator();
assertThat(iterator.next().getId()).isEqualTo("A");
assertThat(iterator.hasNext()).isFalse();
}

@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsReversePostOrder")
@DisplayName("reversePostOrderIterator matches expected order")
@ParameterizedTest(name = "{index} - {0} {2}")
void reversePostOrderIterator(String name, PropertyGraph<String> graph, String[] start, List<String> expectedOrder) {
var iterator = graph.reversePostOrderIterator(start);
for(String expected : expectedOrder) {
String result = iterator.next().getId();
assertThat(result).isEqualTo(expected);
}
}

@MethodSource("com.github.moaxcp.graphs.testframework.MethodSources#graphsReversePostOrder")
@DisplayName("reversePostOrderStream matches expected order")
@ParameterizedTest(name = "{index} - {0} {2}")
void reversePostOrderStream(String name, PropertyGraph<String> graph, String[] start, List<String> expectedOrder) {
var result = graph.reversePostOrderStream(start)
.map(Vertex::getId)
.collect(toList());

assertThat(result).isEqualTo(expectedOrder);
}
}
Loading

0 comments on commit c9629cb

Please sign in to comment.