Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
39 changed files
with
2286 additions
and
4 deletions
... | @@ -45,7 +45,7 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E | ... | @@ -45,7 +45,7 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E |
45 | for (L listener : listeners) { | 45 | for (L listener : listeners) { |
46 | try { | 46 | try { |
47 | listener.event(event); | 47 | listener.event(event); |
48 | - } catch (Throwable error) { | 48 | + } catch (Exception error) { |
49 | reportProblem(event, error); | 49 | reportProblem(event, error); |
50 | } | 50 | } |
51 | } | 51 | } | ... | ... |
... | @@ -82,7 +82,7 @@ public class SimpleEventDispatcher extends DefaultEventSinkRegistry | ... | @@ -82,7 +82,7 @@ public class SimpleEventDispatcher extends DefaultEventSinkRegistry |
82 | log.warn("No sink registered for event class {}", | 82 | log.warn("No sink registered for event class {}", |
83 | event.getClass()); | 83 | event.getClass()); |
84 | } | 84 | } |
85 | - } catch (Throwable e) { | 85 | + } catch (Exception e) { |
86 | log.warn("Error encountered while dispatching event:", e); | 86 | log.warn("Error encountered while dispatching event:", e); |
87 | } | 87 | } |
88 | } | 88 | } | ... | ... |
... | @@ -141,7 +141,6 @@ public class SimpleDeviceManager | ... | @@ -141,7 +141,6 @@ public class SimpleDeviceManager |
141 | public void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions) { | 141 | public void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions) { |
142 | checkNotNull(deviceId, DEVICE_ID_NULL); | 142 | checkNotNull(deviceId, DEVICE_ID_NULL); |
143 | checkNotNull(portDescriptions, "Port descriptions list cannot be null"); | 143 | checkNotNull(portDescriptions, "Port descriptions list cannot be null"); |
144 | - // FIXME: fix the interface to accept DeviceId separately | ||
145 | log.info("Device {} ports updated: {}", portDescriptions); | 144 | log.info("Device {} ports updated: {}", portDescriptions); |
146 | List<DeviceEvent> events = store.updatePorts(deviceId, portDescriptions); | 145 | List<DeviceEvent> events = store.updatePorts(deviceId, portDescriptions); |
147 | for (DeviceEvent event : events) { | 146 | for (DeviceEvent event : events) { | ... | ... |
... | @@ -306,7 +306,7 @@ | ... | @@ -306,7 +306,7 @@ |
306 | <group> | 306 | <group> |
307 | <title>Utilities</title> | 307 | <title>Utilities</title> |
308 | <packages> | 308 | <packages> |
309 | - org.onlab.util:org.onlab.util.* | 309 | + org.onlab.* |
310 | </packages> | 310 | </packages> |
311 | </group> | 311 | </group> |
312 | <group> | 312 | <group> | ... | ... |
... | @@ -17,6 +17,12 @@ | ... | @@ -17,6 +17,12 @@ |
17 | <description>Miscellaneous ON.Lab utilities</description> | 17 | <description>Miscellaneous ON.Lab utilities</description> |
18 | 18 | ||
19 | <dependencies> | 19 | <dependencies> |
20 | + <dependency> | ||
21 | + <groupId>com.google.guava</groupId> | ||
22 | + <artifactId>guava-testlib</artifactId> | ||
23 | + <version>17.0</version> | ||
24 | + <scope>test</scope> | ||
25 | + </dependency> | ||
20 | </dependencies> | 26 | </dependencies> |
21 | 27 | ||
22 | </project> | 28 | </project> | ... | ... |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
6 | + | ||
7 | +/** | ||
8 | + * Abstract graph edge implementation. | ||
9 | + */ | ||
10 | +public abstract class AbstractEdge<V extends Vertex> implements Edge<V> { | ||
11 | + | ||
12 | + private final V src; | ||
13 | + private final V dst; | ||
14 | + | ||
15 | + /** | ||
16 | + * Creates a new edge between the specified source and destination vertexes. | ||
17 | + * | ||
18 | + * @param src source vertex | ||
19 | + * @param dst destination vertex | ||
20 | + */ | ||
21 | + public AbstractEdge(V src, V dst) { | ||
22 | + this.src = checkNotNull(src, "Source vertex cannot be null"); | ||
23 | + this.dst = checkNotNull(dst, "Destination vertex cannot be null"); | ||
24 | + } | ||
25 | + | ||
26 | + @Override | ||
27 | + public V src() { | ||
28 | + return src; | ||
29 | + } | ||
30 | + | ||
31 | + @Override | ||
32 | + public V dst() { | ||
33 | + return dst; | ||
34 | + } | ||
35 | + | ||
36 | + @Override | ||
37 | + public int hashCode() { | ||
38 | + return Objects.hash(src, dst); | ||
39 | + } | ||
40 | + | ||
41 | + @Override | ||
42 | + public boolean equals(Object obj) { | ||
43 | + if (obj instanceof AbstractEdge) { | ||
44 | + final AbstractEdge other = (AbstractEdge) obj; | ||
45 | + return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst); | ||
46 | + } | ||
47 | + return false; | ||
48 | + } | ||
49 | + | ||
50 | + @Override | ||
51 | + public String toString() { | ||
52 | + return com.google.common.base.Objects.toStringHelper(this) | ||
53 | + .add("src", src) | ||
54 | + .add("dst", dst) | ||
55 | + .toString(); | ||
56 | + } | ||
57 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.HashMap; | ||
4 | +import java.util.HashSet; | ||
5 | +import java.util.Iterator; | ||
6 | +import java.util.Map; | ||
7 | +import java.util.Set; | ||
8 | + | ||
9 | +import static com.google.common.base.Preconditions.checkArgument; | ||
10 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
11 | + | ||
12 | +/** | ||
13 | + * Basis for various graph path search algorithm implementations. | ||
14 | + * | ||
15 | + * @param <V> vertex type | ||
16 | + * @param <E> edge type | ||
17 | + */ | ||
18 | +public abstract class AbstractGraphPathSearch<V extends Vertex, E extends Edge<V>> | ||
19 | + implements GraphPathSearch<V, E> { | ||
20 | + | ||
21 | + private double samenessThreshold = 0.000000001; | ||
22 | + | ||
23 | + /** | ||
24 | + * Sets a new sameness threshold for comparing cost values; default is | ||
25 | + * is {@code 0.000000001}. | ||
26 | + * | ||
27 | + * @param threshold fractional double value | ||
28 | + */ | ||
29 | + public void setSamenessThreshold(double threshold) { | ||
30 | + samenessThreshold = threshold; | ||
31 | + } | ||
32 | + | ||
33 | + /** | ||
34 | + * Returns the current sameness threshold for comparing cost values. | ||
35 | + * | ||
36 | + * @return current threshold | ||
37 | + */ | ||
38 | + public double samenessThreshold() { | ||
39 | + return samenessThreshold; | ||
40 | + } | ||
41 | + | ||
42 | + /** | ||
43 | + * Default path search result that uses the DefaultPath to convey paths | ||
44 | + * in a graph. | ||
45 | + */ | ||
46 | + protected class DefaultResult implements Result<V, E> { | ||
47 | + | ||
48 | + private final V src; | ||
49 | + private final V dst; | ||
50 | + protected final Set<Path<V, E>> paths = new HashSet<>(); | ||
51 | + protected final Map<V, Double> costs = new HashMap<>(); | ||
52 | + protected final Map<V, Set<E>> parents = new HashMap<>(); | ||
53 | + | ||
54 | + /** | ||
55 | + * Creates the result of path search. | ||
56 | + * | ||
57 | + * @param src path source | ||
58 | + * @param dst optional path destination | ||
59 | + */ | ||
60 | + public DefaultResult(V src, V dst) { | ||
61 | + checkNotNull(src, "Source cannot be null"); | ||
62 | + this.src = src; | ||
63 | + this.dst = dst; | ||
64 | + } | ||
65 | + | ||
66 | + @Override | ||
67 | + public V src() { | ||
68 | + return src; | ||
69 | + } | ||
70 | + | ||
71 | + @Override | ||
72 | + public V dst() { | ||
73 | + return dst; | ||
74 | + } | ||
75 | + | ||
76 | + @Override | ||
77 | + public Set<Path<V, E>> paths() { | ||
78 | + return paths; | ||
79 | + } | ||
80 | + | ||
81 | + @Override | ||
82 | + public Map<V, Double> costs() { | ||
83 | + return costs; | ||
84 | + } | ||
85 | + | ||
86 | + @Override | ||
87 | + public Map<V, Set<E>> parents() { | ||
88 | + return parents; | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * Indicates whether or not the given vertex has a cost yet. | ||
93 | + * | ||
94 | + * @param v vertex to test | ||
95 | + * @return true if the vertex has cost already | ||
96 | + */ | ||
97 | + boolean hasCost(V v) { | ||
98 | + return costs.get(v) != null; | ||
99 | + } | ||
100 | + | ||
101 | + /** | ||
102 | + * Returns the current cost to reach the specified vertex. | ||
103 | + * | ||
104 | + * @return cost to reach the vertex | ||
105 | + */ | ||
106 | + double cost(V v) { | ||
107 | + Double c = costs.get(v); | ||
108 | + return c == null ? Double.MAX_VALUE : c; | ||
109 | + } | ||
110 | + | ||
111 | + /** | ||
112 | + * Updates the cost of the vertex using its existing cost plus the | ||
113 | + * cost to traverse the specified edge. | ||
114 | + * | ||
115 | + * @param v vertex | ||
116 | + * @param edge edge through which vertex is reached | ||
117 | + * @param cost current cost to reach the vertex from the source | ||
118 | + * @param replace true to indicate that any accrued edges are to be | ||
119 | + * cleared; false to indicate that the edge should be | ||
120 | + * added to the previously accrued edges as they yield | ||
121 | + * the same cost | ||
122 | + */ | ||
123 | + void updateVertex(V v, E edge, double cost, boolean replace) { | ||
124 | + costs.put(v, cost); | ||
125 | + if (edge != null) { | ||
126 | + Set<E> edges = parents.get(v); | ||
127 | + if (edges == null) { | ||
128 | + edges = new HashSet<>(); | ||
129 | + parents.put(v, edges); | ||
130 | + } | ||
131 | + if (replace) { | ||
132 | + edges.clear(); | ||
133 | + } | ||
134 | + edges.add(edge); | ||
135 | + } | ||
136 | + } | ||
137 | + | ||
138 | + /** | ||
139 | + * Removes the set of parent edges for the specified vertex. | ||
140 | + * | ||
141 | + * @param v vertex | ||
142 | + */ | ||
143 | + void removeVertex(V v) { | ||
144 | + parents.remove(v); | ||
145 | + } | ||
146 | + | ||
147 | + /** | ||
148 | + * If possible, relax the specified edge using the supplied base cost | ||
149 | + * and edge-weight function. | ||
150 | + * | ||
151 | + * @param e edge to be relaxed | ||
152 | + * @param cost base cost to reach the edge destination vertex | ||
153 | + * @param ew optional edge weight function | ||
154 | + * @return true if the edge was relaxed; false otherwise | ||
155 | + */ | ||
156 | + boolean relaxEdge(E e, double cost, EdgeWeight<V, E> ew) { | ||
157 | + V v = e.dst(); | ||
158 | + double oldCost = cost(v); | ||
159 | + double newCost = cost + (ew == null ? 1.0 : ew.weight(e)); | ||
160 | + boolean relaxed = newCost < oldCost; | ||
161 | + boolean same = Math.abs(newCost - oldCost) < samenessThreshold; | ||
162 | + if (same || relaxed) { | ||
163 | + updateVertex(v, e, newCost, !same); | ||
164 | + } | ||
165 | + return relaxed; | ||
166 | + } | ||
167 | + | ||
168 | + /** | ||
169 | + * Builds a set of paths for the specified src/dst vertex pair. | ||
170 | + */ | ||
171 | + protected void buildPaths() { | ||
172 | + Set<V> destinations = new HashSet<>(); | ||
173 | + if (dst == null) { | ||
174 | + destinations.addAll(costs.keySet()); | ||
175 | + } else { | ||
176 | + destinations.add(dst); | ||
177 | + } | ||
178 | + | ||
179 | + // Build all paths between the source and all requested destinations. | ||
180 | + for (V v : destinations) { | ||
181 | + // Ignore the source, if it is among the destinations. | ||
182 | + if (!v.equals(src)) { | ||
183 | + buildAllPaths(this, src, v); | ||
184 | + } | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + } | ||
189 | + | ||
190 | + /** | ||
191 | + * Builds a set of all paths between the source and destination using the | ||
192 | + * graph search result by applying breadth-first search through the parent | ||
193 | + * edges and vertex costs. | ||
194 | + * | ||
195 | + * @param result graph search result | ||
196 | + * @param src source vertex | ||
197 | + * @param dst destination vertex | ||
198 | + */ | ||
199 | + private void buildAllPaths(DefaultResult result, V src, V dst) { | ||
200 | + DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>(); | ||
201 | + basePath.setCost(result.cost(dst)); | ||
202 | + | ||
203 | + Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>(); | ||
204 | + pendingPaths.add(basePath); | ||
205 | + | ||
206 | + while (!pendingPaths.isEmpty()) { | ||
207 | + Set<DefaultMutablePath<V, E>> frontier = new HashSet<>(); | ||
208 | + | ||
209 | + for (DefaultMutablePath<V, E> path : pendingPaths) { | ||
210 | + // For each pending path, locate its first vertex since we | ||
211 | + // will be moving backwards from it. | ||
212 | + V firstVertex = firstVertex(path, dst); | ||
213 | + | ||
214 | + // If the first vertex is our expected source, we have reached | ||
215 | + // the beginning, so add the this path to the result paths. | ||
216 | + if (firstVertex.equals(src)) { | ||
217 | + path.setCost(result.cost(dst)); | ||
218 | + result.paths.add(new DefaultPath<>(path.edges(), path.cost())); | ||
219 | + | ||
220 | + } else { | ||
221 | + // If we have not reached the beginning, i.e. the source, | ||
222 | + // fetch the set of edges leading to the first vertex of | ||
223 | + // this pending path; if there are none, abandon processing | ||
224 | + // this path for good. | ||
225 | + Set<E> firstVertexParents = result.parents.get(firstVertex); | ||
226 | + if (firstVertexParents == null || firstVertexParents.isEmpty()) { | ||
227 | + break; | ||
228 | + } | ||
229 | + | ||
230 | + // Now iterate over all the edges and for each of them | ||
231 | + // cloning the current path and then insert that edge to | ||
232 | + // the path and then add that path to the pending ones. | ||
233 | + // When processing the last edge, modify the current | ||
234 | + // pending path rather than cloning a new one. | ||
235 | + Iterator<E> edges = firstVertexParents.iterator(); | ||
236 | + while (edges.hasNext()) { | ||
237 | + E edge = edges.next(); | ||
238 | + boolean isLast = !edges.hasNext(); | ||
239 | + DefaultMutablePath<V, E> pendingPath = isLast ? path : new DefaultMutablePath<>(path); | ||
240 | + pendingPath.insertEdge(edge); | ||
241 | + frontier.add(pendingPath); | ||
242 | + } | ||
243 | + } | ||
244 | + } | ||
245 | + | ||
246 | + // All pending paths have been scanned so promote the next frontier | ||
247 | + pendingPaths = frontier; | ||
248 | + } | ||
249 | + } | ||
250 | + | ||
251 | + // Returns the first vertex of the specified path. This is either the source | ||
252 | + // of the first edge or, if there are no edges yet, the given destination. | ||
253 | + private V firstVertex(Path<V, E> path, V dst) { | ||
254 | + return path.edges().isEmpty() ? dst : path.edges().get(0).src(); | ||
255 | + } | ||
256 | + | ||
257 | + /** | ||
258 | + * Checks the specified path search arguments for validity. | ||
259 | + * | ||
260 | + * @param graph graph; must not be null | ||
261 | + * @param src source vertex; must not be null and belong to graph | ||
262 | + * @param dst optional target vertex; must belong to graph | ||
263 | + */ | ||
264 | + protected void checkArguments(Graph<V, E> graph, V src, V dst) { | ||
265 | + checkNotNull(graph, "Graph cannot be null"); | ||
266 | + checkNotNull(src, "Source cannot be null"); | ||
267 | + Set<V> vertices = graph.getVertexes(); | ||
268 | + checkArgument(vertices.contains(src), "Source not in the graph"); | ||
269 | + checkArgument(dst == null || vertices.contains(dst), | ||
270 | + "Destination not in graph"); | ||
271 | + } | ||
272 | + | ||
273 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.ImmutableSet; | ||
4 | +import com.google.common.collect.ImmutableSetMultimap; | ||
5 | + | ||
6 | +import java.util.Objects; | ||
7 | +import java.util.Set; | ||
8 | + | ||
9 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
10 | + | ||
11 | +/** | ||
12 | + * Immutable graph implemented using adjacency lists. | ||
13 | + * | ||
14 | + * @param <V> vertex type | ||
15 | + * @param <E> edge type | ||
16 | + */ | ||
17 | +public class AdjacencyListsGraph<V extends Vertex, E extends Edge<V>> implements Graph<V, E> { | ||
18 | + | ||
19 | + private final Set<V> vertexes; | ||
20 | + private final Set<E> edges; | ||
21 | + | ||
22 | + private final ImmutableSetMultimap<V, E> sources; | ||
23 | + private final ImmutableSetMultimap<V, E> destinations; | ||
24 | + | ||
25 | + /** | ||
26 | + * Creates a graph comprising of the specified vertexes and edges. | ||
27 | + * | ||
28 | + * @param vertexes set of graph vertexes | ||
29 | + * @param edges set of graph edges | ||
30 | + */ | ||
31 | + public AdjacencyListsGraph(Set<V> vertexes, Set<E> edges) { | ||
32 | + checkNotNull(vertexes, "Vertex set cannot be null"); | ||
33 | + checkNotNull(edges, "Edge set cannot be null"); | ||
34 | + | ||
35 | + // Record ingress/egress edges for each vertex. | ||
36 | + ImmutableSetMultimap.Builder<V, E> srcMap = ImmutableSetMultimap.builder(); | ||
37 | + ImmutableSetMultimap.Builder<V, E> dstMap = ImmutableSetMultimap.builder(); | ||
38 | + | ||
39 | + // Also make sure that all edge end-points are added as vertexes | ||
40 | + ImmutableSet.Builder<V> actualVertexes = ImmutableSet.builder(); | ||
41 | + actualVertexes.addAll(vertexes); | ||
42 | + | ||
43 | + for (E edge : edges) { | ||
44 | + srcMap.put(edge.src(), edge); | ||
45 | + actualVertexes.add(edge.src()); | ||
46 | + dstMap.put(edge.dst(), edge); | ||
47 | + actualVertexes.add(edge.dst()); | ||
48 | + } | ||
49 | + | ||
50 | + // Make an immutable copy of the edge and vertex sets | ||
51 | + this.edges = ImmutableSet.copyOf(edges); | ||
52 | + this.vertexes = actualVertexes.build(); | ||
53 | + | ||
54 | + // Build immutable copies of sources and destinations edge maps | ||
55 | + sources = srcMap.build(); | ||
56 | + destinations = dstMap.build(); | ||
57 | + } | ||
58 | + | ||
59 | + @Override | ||
60 | + public Set<V> getVertexes() { | ||
61 | + return vertexes; | ||
62 | + } | ||
63 | + | ||
64 | + @Override | ||
65 | + public Set<E> getEdges() { | ||
66 | + return edges; | ||
67 | + } | ||
68 | + | ||
69 | + @Override | ||
70 | + public Set<E> getEdgesFrom(V src) { | ||
71 | + return sources.get(src); | ||
72 | + } | ||
73 | + | ||
74 | + @Override | ||
75 | + public Set<E> getEdgesTo(V dst) { | ||
76 | + return destinations.get(dst); | ||
77 | + } | ||
78 | + | ||
79 | + @Override | ||
80 | + public boolean equals(Object obj) { | ||
81 | + if (obj instanceof AdjacencyListsGraph) { | ||
82 | + AdjacencyListsGraph that = (AdjacencyListsGraph) obj; | ||
83 | + return this.getClass() == that.getClass() && | ||
84 | + Objects.equals(this.vertexes, that.vertexes) && | ||
85 | + Objects.equals(this.edges, that.edges); | ||
86 | + } | ||
87 | + return false; | ||
88 | + } | ||
89 | + | ||
90 | + @Override | ||
91 | + public int hashCode() { | ||
92 | + return Objects.hash(vertexes, edges); | ||
93 | + } | ||
94 | + | ||
95 | + @Override | ||
96 | + public String toString() { | ||
97 | + return com.google.common.base.Objects.toStringHelper(this) | ||
98 | + .add("vertexes", vertexes) | ||
99 | + .add("edges", edges) | ||
100 | + .toString(); | ||
101 | + } | ||
102 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Bellman-Ford graph search algorithm for locating shortest-paths in | ||
5 | + * directed graphs that may contain negative cycles. | ||
6 | + */ | ||
7 | +public class BellmanFordGraphSearch<V extends Vertex, E extends Edge<V>> | ||
8 | + extends AbstractGraphPathSearch<V, E> { | ||
9 | + | ||
10 | + @Override | ||
11 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, | ||
12 | + EdgeWeight<V, E> weight) { | ||
13 | + checkArguments(graph, src, dst); | ||
14 | + | ||
15 | + // Prepare the graph search result. | ||
16 | + DefaultResult result = new DefaultResult(src, dst); | ||
17 | + | ||
18 | + // The source vertex has cost 0, of course. | ||
19 | + result.updateVertex(src, null, 0.0, true); | ||
20 | + | ||
21 | + int max = graph.getVertexes().size() - 1; | ||
22 | + for (int i = 0; i < max; i++) { | ||
23 | + // Relax, if possible, all egress edges of the current vertex. | ||
24 | + for (E edge : graph.getEdges()) { | ||
25 | + if (result.hasCost(edge.src())) { | ||
26 | + result.relaxEdge(edge, result.cost(edge.src()), weight); | ||
27 | + } | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + // Remove any vertexes reached by traversing edges with negative weights. | ||
32 | + for (E edge : graph.getEdges()) { | ||
33 | + if (result.hasCost(edge.src())) { | ||
34 | + if (result.relaxEdge(edge, result.cost(edge.src()), weight)) { | ||
35 | + result.removeVertex(edge.dst()); | ||
36 | + } | ||
37 | + } | ||
38 | + } | ||
39 | + | ||
40 | + // Finally, but the paths on the search result and return. | ||
41 | + result.buildPaths(); | ||
42 | + return result; | ||
43 | + } | ||
44 | + | ||
45 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.HashSet; | ||
4 | +import java.util.Set; | ||
5 | + | ||
6 | +/** | ||
7 | + * Implementation of the BFS algorithm. | ||
8 | + */ | ||
9 | +public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> | ||
10 | + extends AbstractGraphPathSearch<V, E> { | ||
11 | + | ||
12 | + @Override | ||
13 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, | ||
14 | + EdgeWeight<V, E> weight) { | ||
15 | + checkArguments(graph, src, dst); | ||
16 | + | ||
17 | + // Prepare the graph result. | ||
18 | + DefaultResult result = new DefaultResult(src, dst); | ||
19 | + | ||
20 | + // Setup the starting frontier with the source as the sole vertex. | ||
21 | + Set<V> frontier = new HashSet<>(); | ||
22 | + result.updateVertex(src, null, 0.0, true); | ||
23 | + frontier.add(src); | ||
24 | + | ||
25 | + boolean reachedEnd = false; | ||
26 | + while (!reachedEnd && !frontier.isEmpty()) { | ||
27 | + // Prepare the next frontier. | ||
28 | + Set<V> next = new HashSet<>(); | ||
29 | + | ||
30 | + // Visit all vertexes in the current frontier. | ||
31 | + for (V vertex : frontier) { | ||
32 | + double cost = result.cost(vertex); | ||
33 | + | ||
34 | + // Visit all egress edges of the current frontier vertex. | ||
35 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
36 | + V nextVertex = edge.dst(); | ||
37 | + if (!result.hasCost(nextVertex)) { | ||
38 | + // If this vertex has not been visited yet, update it. | ||
39 | + double newCost = cost + (weight == null ? 1.0 : weight.weight(edge)); | ||
40 | + result.updateVertex(nextVertex, edge, newCost, true); | ||
41 | + // If we have reached our intended destination, bail. | ||
42 | + if (nextVertex.equals(dst)) { | ||
43 | + reachedEnd = true; | ||
44 | + break; | ||
45 | + } | ||
46 | + next.add(nextVertex); | ||
47 | + } | ||
48 | + | ||
49 | + if (reachedEnd) { | ||
50 | + break; | ||
51 | + } | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | + // Promote the next frontier. | ||
56 | + frontier = next; | ||
57 | + } | ||
58 | + | ||
59 | + // Finally, but the paths on the search result and return. | ||
60 | + result.buildPaths(); | ||
61 | + return result; | ||
62 | + } | ||
63 | + | ||
64 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.ImmutableList; | ||
4 | + | ||
5 | +import java.util.ArrayList; | ||
6 | +import java.util.List; | ||
7 | +import java.util.Objects; | ||
8 | + | ||
9 | +import static com.google.common.base.Preconditions.checkArgument; | ||
10 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
11 | + | ||
12 | +/** | ||
13 | + * Simple concrete implementation of a directed graph path. | ||
14 | + */ | ||
15 | +public class DefaultMutablePath<V extends Vertex, E extends Edge<V>> implements MutablePath<V, E> { | ||
16 | + | ||
17 | + private final List<E> edges = new ArrayList<>(); | ||
18 | + private double cost = 0.0; | ||
19 | + | ||
20 | + /** | ||
21 | + * Creates a new empty path. | ||
22 | + */ | ||
23 | + public DefaultMutablePath() { | ||
24 | + } | ||
25 | + | ||
26 | + /** | ||
27 | + * Creates a new path as a copy of another path. | ||
28 | + * | ||
29 | + * @param path path to be copied | ||
30 | + */ | ||
31 | + public DefaultMutablePath(Path<V, E> path) { | ||
32 | + checkNotNull(path, "Path cannot be null"); | ||
33 | + this.cost = path.cost(); | ||
34 | + edges.addAll(path.edges()); | ||
35 | + } | ||
36 | + | ||
37 | + @Override | ||
38 | + public V src() { | ||
39 | + return edges.isEmpty() ? null : edges.get(0).src(); | ||
40 | + } | ||
41 | + | ||
42 | + @Override | ||
43 | + public V dst() { | ||
44 | + return edges.isEmpty() ? null : edges.get(edges.size() - 1).dst(); | ||
45 | + } | ||
46 | + | ||
47 | + @Override | ||
48 | + public double cost() { | ||
49 | + return cost; | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + public List<E> edges() { | ||
54 | + return ImmutableList.copyOf(edges); | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
58 | + public void setCost(double cost) { | ||
59 | + this.cost = cost; | ||
60 | + } | ||
61 | + | ||
62 | + @Override | ||
63 | + public Path<V, E> toImmutable() { | ||
64 | + return new DefaultPath<>(edges, cost); | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + public void insertEdge(E edge) { | ||
69 | + checkNotNull(edge, "Edge cannot be null"); | ||
70 | + checkArgument(edges.isEmpty() || src().equals(edge.dst()), | ||
71 | + "Edge destination must be the same as the current path source"); | ||
72 | + edges.add(0, edge); | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public void appendEdge(E edge) { | ||
77 | + checkNotNull(edge, "Edge cannot be null"); | ||
78 | + checkArgument(edges.isEmpty() || dst().equals(edge.src()), | ||
79 | + "Edge source must be the same as the current path destination"); | ||
80 | + edges.add(edge); | ||
81 | + } | ||
82 | + | ||
83 | + @Override | ||
84 | + public void removeEdge(E edge) { | ||
85 | + checkArgument(edge.src().equals(edge.dst()) || | ||
86 | + edges.indexOf(edge) == 0 || | ||
87 | + edges.lastIndexOf(edge) == edges.size() - 1, | ||
88 | + "Edge must be at start or end of path, or it must be a cyclic edge"); | ||
89 | + edges.remove(edge); | ||
90 | + } | ||
91 | + | ||
92 | + @Override | ||
93 | + public String toString() { | ||
94 | + return com.google.common.base.Objects.toStringHelper(this) | ||
95 | + .add("src", src()) | ||
96 | + .add("dst", dst()) | ||
97 | + .add("cost", cost) | ||
98 | + .add("edges", edges) | ||
99 | + .toString(); | ||
100 | + } | ||
101 | + | ||
102 | + @Override | ||
103 | + public int hashCode() { | ||
104 | + return Objects.hash(edges, cost); | ||
105 | + } | ||
106 | + | ||
107 | + @Override | ||
108 | + public boolean equals(Object obj) { | ||
109 | + if (obj instanceof DefaultMutablePath) { | ||
110 | + final DefaultMutablePath other = (DefaultMutablePath) obj; | ||
111 | + return Objects.equals(this.cost, other.cost) && | ||
112 | + Objects.equals(this.edges, other.edges); | ||
113 | + } | ||
114 | + return false; | ||
115 | + } | ||
116 | + | ||
117 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.ImmutableList; | ||
4 | + | ||
5 | +import java.util.Collections; | ||
6 | +import java.util.List; | ||
7 | +import java.util.Objects; | ||
8 | + | ||
9 | +import static com.google.common.base.Preconditions.checkArgument; | ||
10 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
11 | + | ||
12 | +/** | ||
13 | + * Simple concrete implementation of a directed graph path. | ||
14 | + */ | ||
15 | +public class DefaultPath<V extends Vertex, E extends Edge<V>> implements Path<V, E> { | ||
16 | + | ||
17 | + private final V src; | ||
18 | + private final V dst; | ||
19 | + private final List<E> edges; | ||
20 | + private double cost = 0.0; | ||
21 | + | ||
22 | + /** | ||
23 | + * Creates a new path from the specified list of edges and cost. | ||
24 | + * | ||
25 | + * @param edges list of path edges | ||
26 | + * @param cost path cost as a unit-less number | ||
27 | + */ | ||
28 | + public DefaultPath(List<E> edges, double cost) { | ||
29 | + checkNotNull(edges, "Edges list must not be null"); | ||
30 | + checkArgument(!edges.isEmpty(), "There must be at least one edge"); | ||
31 | + this.edges = ImmutableList.copyOf(edges); | ||
32 | + this.src = edges.get(0).src(); | ||
33 | + this.dst = edges.get(edges.size() - 1).dst(); | ||
34 | + this.cost = cost; | ||
35 | + } | ||
36 | + | ||
37 | + @Override | ||
38 | + public V src() { | ||
39 | + return src; | ||
40 | + } | ||
41 | + | ||
42 | + @Override | ||
43 | + public V dst() { | ||
44 | + return dst; | ||
45 | + } | ||
46 | + | ||
47 | + @Override | ||
48 | + public double cost() { | ||
49 | + return cost; | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + public List<E> edges() { | ||
54 | + return Collections.unmodifiableList(edges); | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
58 | + public String toString() { | ||
59 | + return com.google.common.base.Objects.toStringHelper(this) | ||
60 | + .add("src", src) | ||
61 | + .add("dst", dst) | ||
62 | + .add("cost", cost) | ||
63 | + .add("edges", edges) | ||
64 | + .toString(); | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + public int hashCode() { | ||
69 | + return Objects.hash(src, dst, edges, cost); | ||
70 | + } | ||
71 | + | ||
72 | + @Override | ||
73 | + public boolean equals(Object obj) { | ||
74 | + if (obj instanceof DefaultPath) { | ||
75 | + final DefaultPath other = (DefaultPath) obj; | ||
76 | + return Objects.equals(this.src, other.src) && | ||
77 | + Objects.equals(this.dst, other.dst) && | ||
78 | + Objects.equals(this.cost, other.cost) && | ||
79 | + Objects.equals(this.edges, other.edges); | ||
80 | + } | ||
81 | + return false; | ||
82 | + } | ||
83 | + | ||
84 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.HashMap; | ||
4 | +import java.util.HashSet; | ||
5 | +import java.util.Map; | ||
6 | +import java.util.Set; | ||
7 | +import java.util.Stack; | ||
8 | + | ||
9 | +/** | ||
10 | + * DFS graph search algorithm implemented via iteration rather than recursion. | ||
11 | + */ | ||
12 | +public class DepthFirstSearch<V extends Vertex, E extends Edge<V>> | ||
13 | + extends AbstractGraphPathSearch<V, E> { | ||
14 | + | ||
15 | + /** | ||
16 | + * Graph edge types as classified by the DFS algorithm. | ||
17 | + */ | ||
18 | + public static enum EdgeType { | ||
19 | + TREE_EDGE, FORWARD_EDGE, BACK_EDGE, CROSS_EDGE | ||
20 | + } | ||
21 | + | ||
22 | + @Override | ||
23 | + public SpanningTreeResult search(Graph<V, E> graph, V src, V dst, | ||
24 | + EdgeWeight<V, E> weight) { | ||
25 | + checkArguments(graph, src, dst); | ||
26 | + | ||
27 | + // Prepare the search result. | ||
28 | + SpanningTreeResult result = new SpanningTreeResult(src, dst); | ||
29 | + | ||
30 | + // The source vertex has cost 0, of course. | ||
31 | + result.updateVertex(src, null, 0.0, true); | ||
32 | + | ||
33 | + // Track finished vertexes and keep a stack of vertexes that have been | ||
34 | + // started; start this stack with the source on it. | ||
35 | + Set<V> finished = new HashSet<>(); | ||
36 | + Stack<V> stack = new Stack<>(); | ||
37 | + stack.push(src); | ||
38 | + | ||
39 | + while (!stack.isEmpty()) { | ||
40 | + V vertex = stack.peek(); | ||
41 | + if (vertex.equals(dst)) { | ||
42 | + // If we have reached our destination, bail. | ||
43 | + break; | ||
44 | + } | ||
45 | + | ||
46 | + double cost = result.cost(vertex); | ||
47 | + boolean tangent = false; | ||
48 | + | ||
49 | + // Visit all egress edges of the current vertex. | ||
50 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
51 | + // If we have seen the edge already, skip it. | ||
52 | + if (result.isEdgeMarked(edge)) { | ||
53 | + continue; | ||
54 | + } | ||
55 | + | ||
56 | + // Examine the destination of the current edge. | ||
57 | + V nextVertex = edge.dst(); | ||
58 | + if (!result.hasCost(nextVertex)) { | ||
59 | + // If this vertex have not finished this vertex yet, | ||
60 | + // not started it, then start it as a tree-edge. | ||
61 | + result.markEdge(edge, EdgeType.TREE_EDGE); | ||
62 | + double newCost = cost + (weight == null ? 1.0 : weight.weight(edge)); | ||
63 | + result.updateVertex(nextVertex, edge, newCost, true); | ||
64 | + stack.push(nextVertex); | ||
65 | + tangent = true; | ||
66 | + break; | ||
67 | + | ||
68 | + } else if (!finished.contains(nextVertex)) { | ||
69 | + // We started the vertex, but did not yet finish it, so | ||
70 | + // it must be a back-edge. | ||
71 | + result.markEdge(edge, EdgeType.BACK_EDGE); | ||
72 | + } else { | ||
73 | + // The target has been finished already, so what we have | ||
74 | + // here is either a forward-edge or a cross-edge. | ||
75 | + result.markEdge(edge, isForwardEdge(result, edge) ? | ||
76 | + EdgeType.FORWARD_EDGE : EdgeType.CROSS_EDGE); | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + // If we have not been sent on a tangent search and reached the | ||
81 | + // end of the current scan normally, mark the node as finished | ||
82 | + // and pop it off the vertex stack. | ||
83 | + if (!tangent) { | ||
84 | + finished.add(vertex); | ||
85 | + stack.pop(); | ||
86 | + } | ||
87 | + } | ||
88 | + | ||
89 | + // Finally, but the paths on the search result and return. | ||
90 | + result.buildPaths(); | ||
91 | + return result; | ||
92 | + } | ||
93 | + | ||
94 | + /** | ||
95 | + * Determines whether the specified edge is a forward edge using the | ||
96 | + * accumulated set of parent edges for each vertex. | ||
97 | + * | ||
98 | + * @param result search result | ||
99 | + * @param edge edge to be classified | ||
100 | + * @return true if the edge is a forward edge | ||
101 | + */ | ||
102 | + protected boolean isForwardEdge(DefaultResult result, E edge) { | ||
103 | + // Follow the parent edges until we hit the edge source vertex | ||
104 | + V target = edge.src(); | ||
105 | + V vertex = edge.dst(); | ||
106 | + Set<E> parentEdges; | ||
107 | + while ((parentEdges = result.parents.get(vertex)) != null) { | ||
108 | + for (E parentEdge : parentEdges) { | ||
109 | + vertex = parentEdge.src(); | ||
110 | + if (vertex.equals(target)) { | ||
111 | + return true; | ||
112 | + } | ||
113 | + } | ||
114 | + } | ||
115 | + return false; | ||
116 | + } | ||
117 | + | ||
118 | + /** | ||
119 | + * Graph search result which includes edge classification for building | ||
120 | + * a spanning tree. | ||
121 | + */ | ||
122 | + public class SpanningTreeResult extends DefaultResult { | ||
123 | + | ||
124 | + protected final Map<E, EdgeType> edges = new HashMap<>(); | ||
125 | + | ||
126 | + /** | ||
127 | + * Creates a new spanning tree result. | ||
128 | + * | ||
129 | + * @param src search source | ||
130 | + * @param dst optional search destination | ||
131 | + */ | ||
132 | + public SpanningTreeResult(V src, V dst) { | ||
133 | + super(src, dst); | ||
134 | + } | ||
135 | + | ||
136 | + /** | ||
137 | + * Returns the map of edge type. | ||
138 | + * | ||
139 | + * @return edge to edge type bindings | ||
140 | + */ | ||
141 | + public Map<E, EdgeType> edges() { | ||
142 | + return edges; | ||
143 | + } | ||
144 | + | ||
145 | + /** | ||
146 | + * Indicates whether or not the edge has been marked with type. | ||
147 | + * | ||
148 | + * @param edge edge to test | ||
149 | + * @return true if the edge has been marked already | ||
150 | + */ | ||
151 | + boolean isEdgeMarked(E edge) { | ||
152 | + return edges.containsKey(edge); | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * Marks the edge with the specified type. | ||
157 | + * | ||
158 | + * @param edge edge to mark | ||
159 | + * @param type edge type | ||
160 | + */ | ||
161 | + void markEdge(E edge, EdgeType type) { | ||
162 | + edges.put(edge, type); | ||
163 | + } | ||
164 | + | ||
165 | + } | ||
166 | + | ||
167 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.ArrayList; | ||
4 | +import java.util.Comparator; | ||
5 | +import java.util.Set; | ||
6 | + | ||
7 | +/** | ||
8 | + * Dijkstra shortest-path graph search algorithm capable of finding not just | ||
9 | + * one, but all shortest paths between the source and destinations. | ||
10 | + */ | ||
11 | +public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> | ||
12 | + extends AbstractGraphPathSearch<V, E> { | ||
13 | + | ||
14 | + @Override | ||
15 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, | ||
16 | + EdgeWeight<V, E> weight) { | ||
17 | + checkArguments(graph, src, dst); | ||
18 | + | ||
19 | + // Use the default result to remember cumulative costs and parent | ||
20 | + // edges to each each respective vertex. | ||
21 | + DefaultResult result = new DefaultResult(src, dst); | ||
22 | + | ||
23 | + // Cost to reach the source vertex is 0 of course. | ||
24 | + result.updateVertex(src, null, 0.0, false); | ||
25 | + | ||
26 | + // Use the min priority queue to progressively find each nearest | ||
27 | + // vertex until we reach the desired destination, if one was given, | ||
28 | + // or until we reach all possible destinations. | ||
29 | + Heap<V> minQueue = createMinQueue(graph.getVertexes(), | ||
30 | + new PathCostComparator(result)); | ||
31 | + while (!minQueue.isEmpty()) { | ||
32 | + // Get the nearest vertex | ||
33 | + V nearest = minQueue.extractExtreme(); | ||
34 | + if (nearest.equals(dst)) { | ||
35 | + break; | ||
36 | + } | ||
37 | + | ||
38 | + // Find its cost and use it to determine if the vertex is reachable. | ||
39 | + double cost = result.cost(nearest); | ||
40 | + if (cost < Double.MAX_VALUE) { | ||
41 | + // If the vertex is reachable, relax all its egress edges. | ||
42 | + for (E e : graph.getEdgesFrom(nearest)) { | ||
43 | + result.relaxEdge(e, cost, weight); | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + // Re-prioritize the min queue. | ||
48 | + minQueue.heapify(); | ||
49 | + } | ||
50 | + | ||
51 | + // Now construct a set of paths from the results. | ||
52 | + result.buildPaths(); | ||
53 | + return result; | ||
54 | + } | ||
55 | + | ||
56 | + // Compares path weights using their accrued costs; used for sorting the | ||
57 | + // min priority queue. | ||
58 | + private final class PathCostComparator implements Comparator<V> { | ||
59 | + private final DefaultResult result; | ||
60 | + | ||
61 | + private PathCostComparator(DefaultResult result) { | ||
62 | + this.result = result; | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + public int compare(V v1, V v2) { | ||
67 | + double delta = result.cost(v2) - result.cost(v1); | ||
68 | + return delta < 0 ? -1 : (delta > 0 ? 1 : 0); | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + // Creates a min priority queue from the specified vertexes and comparator. | ||
73 | + private Heap<V> createMinQueue(Set<V> vertexes, Comparator<V> comparator) { | ||
74 | + return new Heap<>(new ArrayList<>(vertexes), comparator); | ||
75 | + } | ||
76 | + | ||
77 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Representation of a graph edge. | ||
5 | + * | ||
6 | + * @param <V> vertex type | ||
7 | + */ | ||
8 | +public interface Edge<V extends Vertex> { | ||
9 | + | ||
10 | + /** | ||
11 | + * Returns the edge source vertex. | ||
12 | + * | ||
13 | + * @return source vertex | ||
14 | + */ | ||
15 | + V src(); | ||
16 | + | ||
17 | + /** | ||
18 | + * Returns the edge destination vertex. | ||
19 | + * | ||
20 | + * @return destination vertex | ||
21 | + */ | ||
22 | + V dst(); | ||
23 | + | ||
24 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Abstraction of a graph edge weight function. | ||
5 | + */ | ||
6 | +public interface EdgeWeight<V extends Vertex, E extends Edge<V>> { | ||
7 | + | ||
8 | + /** | ||
9 | + * Returns the weight of the given edge as a unit-less number. | ||
10 | + * | ||
11 | + * @param edge edge to be weighed | ||
12 | + * @return edge weight as a unit-less number | ||
13 | + */ | ||
14 | + double weight(E edge); | ||
15 | + | ||
16 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | + | ||
4 | +import java.util.Set; | ||
5 | + | ||
6 | +/** | ||
7 | + * Abstraction of a directed graph structure. | ||
8 | + * | ||
9 | + * @param <V> vertex type | ||
10 | + * @param <E> edge type | ||
11 | + */ | ||
12 | +public interface Graph<V extends Vertex, E extends Edge> { | ||
13 | + | ||
14 | + /** | ||
15 | + * Returns the set of vertexes comprising the graph. | ||
16 | + * | ||
17 | + * @return set of vertexes | ||
18 | + */ | ||
19 | + Set<V> getVertexes(); | ||
20 | + | ||
21 | + /** | ||
22 | + * Returns the set of edges comprising the graph. | ||
23 | + * | ||
24 | + * @return set of edges | ||
25 | + */ | ||
26 | + Set<E> getEdges(); | ||
27 | + | ||
28 | + /** | ||
29 | + * Returns all edges leading out from the specified source vertex. | ||
30 | + * | ||
31 | + * @param src source vertex | ||
32 | + * @return set of egress edges; empty if no such edges | ||
33 | + */ | ||
34 | + Set<E> getEdgesFrom(V src); | ||
35 | + | ||
36 | + /** | ||
37 | + * Returns all edges leading towards the specified destination vertex. | ||
38 | + * | ||
39 | + * @param dst destination vertex | ||
40 | + * @return set of ingress vertexes; empty if no such edges | ||
41 | + */ | ||
42 | + Set<E> getEdgesTo(V dst); | ||
43 | + | ||
44 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.Map; | ||
4 | +import java.util.Set; | ||
5 | + | ||
6 | +/** | ||
7 | + * Representation of a graph path search algorithm. | ||
8 | + * | ||
9 | + * @param <V> vertex type | ||
10 | + * @param <E> edge type | ||
11 | + */ | ||
12 | +public interface GraphPathSearch<V extends Vertex, E extends Edge<V>> { | ||
13 | + | ||
14 | + /** | ||
15 | + * Abstraction of a path search result. | ||
16 | + */ | ||
17 | + public interface Result<V extends Vertex, E extends Edge<V>> { | ||
18 | + | ||
19 | + /** | ||
20 | + * Returns the search source. | ||
21 | + * | ||
22 | + * @return search source | ||
23 | + */ | ||
24 | + public V src(); | ||
25 | + | ||
26 | + /** | ||
27 | + * Returns the search destination, if was was given. | ||
28 | + * | ||
29 | + * @return optional search destination | ||
30 | + */ | ||
31 | + public V dst(); | ||
32 | + | ||
33 | + /** | ||
34 | + * Returns the set of paths produced as a result of the graph search. | ||
35 | + * | ||
36 | + * @return set of paths | ||
37 | + */ | ||
38 | + Set<Path<V, E>> paths(); | ||
39 | + | ||
40 | + /** | ||
41 | + * Returns bindings of each vertex to its parent edges in the path. | ||
42 | + * | ||
43 | + * @return map of vertex to its parent edge bindings | ||
44 | + */ | ||
45 | + public Map<V, Set<E>> parents(); | ||
46 | + | ||
47 | + /** | ||
48 | + * Return a bindings of each vertex to its cost in the path. | ||
49 | + * | ||
50 | + * @return map of vertex to path cost bindings | ||
51 | + */ | ||
52 | + public Map<V, Double> costs(); | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * Searches the specified graph. | ||
57 | + * | ||
58 | + * @param graph graph to be searched | ||
59 | + * @param src optional source vertex | ||
60 | + * @param dst optional destination vertex; if null paths to all vertex | ||
61 | + * destinations will be searched | ||
62 | + * @param weight optional edge-weight; if null cost of each edge will be | ||
63 | + * assumed to be 1.0 | ||
64 | + * @return search results | ||
65 | + */ | ||
66 | + Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> weight); | ||
67 | + | ||
68 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Representation of a graph search algorithm and its outcome. | ||
5 | + * | ||
6 | + * @param <V> vertex type | ||
7 | + * @param <E> edge type | ||
8 | + */ | ||
9 | +public interface GraphSearch<V extends Vertex, E extends Edge<V>> { | ||
10 | + | ||
11 | + /** | ||
12 | + * Notion of a graph search result. | ||
13 | + */ | ||
14 | + public interface Result<V extends Vertex, E extends Edge<V>> { | ||
15 | + } | ||
16 | + | ||
17 | + /** | ||
18 | + * Searches the specified graph. | ||
19 | + * | ||
20 | + * @param graph graph to be searched | ||
21 | + * @param weight optional edge-weight; if null cost of each edge will be | ||
22 | + * assumed to be 1.0 | ||
23 | + * | ||
24 | + * @return search results | ||
25 | + */ | ||
26 | + Result search(Graph<V, E> graph, EdgeWeight<V, E> weight); | ||
27 | + | ||
28 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.ImmutableList; | ||
4 | + | ||
5 | +import java.util.Comparator; | ||
6 | +import java.util.Iterator; | ||
7 | +import java.util.List; | ||
8 | +import java.util.Objects; | ||
9 | + | ||
10 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
11 | + | ||
12 | +/** | ||
13 | + * Implementation of an array-backed heap structure whose sense of order is | ||
14 | + * imposed by the provided comparator. | ||
15 | + * <p/> | ||
16 | + * While this provides similar functionality to {@link java.util.PriorityQueue} | ||
17 | + * data structure, one key difference is that external entities can control | ||
18 | + * when to restore the heap property, which is done through invocation of the | ||
19 | + * {@link #heapify} method. | ||
20 | + * <p/> | ||
21 | + * This class is not thread-safe and care must be taken to prevent concurrent | ||
22 | + * modifications. | ||
23 | + * | ||
24 | + * @param <T> type of the items on the heap | ||
25 | + */ | ||
26 | +public class Heap<T> { | ||
27 | + | ||
28 | + private final List<T> data; | ||
29 | + private final Comparator<T> comparator; | ||
30 | + | ||
31 | + /** | ||
32 | + * Creates a new heap backed by the specified list. In the interest of | ||
33 | + * efficiency, the list should be array-backed. Also, for the same reason, | ||
34 | + * the data is not copied and therefore, the caller must assure that the | ||
35 | + * backing data is not altered in any way. | ||
36 | + * | ||
37 | + * @param data backing data list | ||
38 | + * @param comparator comparator for ordering the heap items | ||
39 | + */ | ||
40 | + public Heap(List<T> data, Comparator<T> comparator) { | ||
41 | + this.data = checkNotNull(data, "Data cannot be null"); | ||
42 | + this.comparator = checkNotNull(comparator, "Comparator cannot be null"); | ||
43 | + heapify(); | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * Restores the heap property by re-arranging the elements in the backing | ||
48 | + * array as necessary following any heap modifications. | ||
49 | + */ | ||
50 | + public void heapify() { | ||
51 | + for (int i = data.size() / 2; i >= 0; i--) { | ||
52 | + heapify(i); | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + /** | ||
57 | + * Returns the current size of the heap. | ||
58 | + * | ||
59 | + * @return number of items in the heap | ||
60 | + */ | ||
61 | + public int size() { | ||
62 | + return data.size(); | ||
63 | + } | ||
64 | + | ||
65 | + /** | ||
66 | + * Returns true if there are no items in the heap. | ||
67 | + * | ||
68 | + * @return true if heap is empty | ||
69 | + */ | ||
70 | + public boolean isEmpty() { | ||
71 | + return data.isEmpty(); | ||
72 | + } | ||
73 | + | ||
74 | + /** | ||
75 | + * Returns the most extreme item in the heap. | ||
76 | + * | ||
77 | + * @return heap extreme or null if the heap is empty | ||
78 | + */ | ||
79 | + public T extreme() { | ||
80 | + return data.isEmpty() ? null : data.get(0); | ||
81 | + } | ||
82 | + | ||
83 | + /** | ||
84 | + * Extracts and returns the most extreme item from the heap. | ||
85 | + * | ||
86 | + * @return heap extreme or null if the heap is empty | ||
87 | + */ | ||
88 | + public T extractExtreme() { | ||
89 | + if (!isEmpty()) { | ||
90 | + T extreme = extreme(); | ||
91 | + | ||
92 | + data.set(0, data.get(data.size() - 1)); | ||
93 | + data.remove(data.size() - 1); | ||
94 | + heapify(); | ||
95 | + return extreme; | ||
96 | + } | ||
97 | + return null; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * Inserts the specified item into the heap and returns the modified heap. | ||
102 | + * | ||
103 | + * @param item item to be inserted | ||
104 | + * @return the heap self | ||
105 | + * @throws IllegalArgumentException if the heap is already full | ||
106 | + */ | ||
107 | + public Heap<T> insert(T item) { | ||
108 | + data.add(item); | ||
109 | + bubbleUp(); | ||
110 | + return this; | ||
111 | + } | ||
112 | + | ||
113 | + /** | ||
114 | + * Returns iterator to traverse the heap level-by-level. This iterator | ||
115 | + * does not permit removal of items. | ||
116 | + * | ||
117 | + * @return non-destructive heap iterator | ||
118 | + */ | ||
119 | + public Iterator<T> iterator() { | ||
120 | + return ImmutableList.copyOf(data).iterator(); | ||
121 | + } | ||
122 | + | ||
123 | + // Bubbles up the last item in the heap to its proper position to restore | ||
124 | + // the heap property. | ||
125 | + private void bubbleUp() { | ||
126 | + int child = data.size() - 1; | ||
127 | + while (child > 0) { | ||
128 | + int parent = child / 2; | ||
129 | + if (comparator.compare(data.get(child), data.get(parent)) < 0) { | ||
130 | + break; | ||
131 | + } | ||
132 | + swap(child, parent); | ||
133 | + child = parent; | ||
134 | + } | ||
135 | + } | ||
136 | + | ||
137 | + // Restores the heap property of the specified heap layer. | ||
138 | + private void heapify(int i) { | ||
139 | + int left = 2 * i + 1; | ||
140 | + int right = 2 * i; | ||
141 | + int extreme = i; | ||
142 | + | ||
143 | + if (left < data.size() && | ||
144 | + comparator.compare(data.get(extreme), data.get(left)) < 0) { | ||
145 | + extreme = left; | ||
146 | + } | ||
147 | + | ||
148 | + if (right < data.size() && | ||
149 | + comparator.compare(data.get(extreme), data.get(right)) < 0) { | ||
150 | + extreme = right; | ||
151 | + } | ||
152 | + | ||
153 | + if (extreme != i) { | ||
154 | + swap(i, extreme); | ||
155 | + heapify(extreme); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + // Swaps two heap items identified by their respective indexes. | ||
160 | + private void swap(int i, int k) { | ||
161 | + T aux = data.get(i); | ||
162 | + data.set(i, data.get(k)); | ||
163 | + data.set(k, aux); | ||
164 | + } | ||
165 | + | ||
166 | + @Override | ||
167 | + public boolean equals(Object obj) { | ||
168 | + if (obj instanceof Heap) { | ||
169 | + Heap that = (Heap) obj; | ||
170 | + return this.getClass() == that.getClass() && | ||
171 | + Objects.equals(this.comparator, that.comparator) && | ||
172 | + Objects.deepEquals(this.data, that.data); | ||
173 | + } | ||
174 | + return false; | ||
175 | + } | ||
176 | + | ||
177 | + @Override | ||
178 | + public int hashCode() { | ||
179 | + return Objects.hash(comparator, data); | ||
180 | + } | ||
181 | + | ||
182 | + @Override | ||
183 | + public String toString() { | ||
184 | + return com.google.common.base.Objects.toStringHelper(this) | ||
185 | + .add("data", data) | ||
186 | + .add("comparator", comparator) | ||
187 | + .toString(); | ||
188 | + } | ||
189 | + | ||
190 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Abstraction of a mutable graph that can be constructed gradually. | ||
5 | + */ | ||
6 | +public interface MutableGraph<V extends Vertex, E extends Edge> extends Graph<V, E> { | ||
7 | + | ||
8 | + /** | ||
9 | + * Adds the specified vertex to this graph. | ||
10 | + * | ||
11 | + * @param vertex new vertex | ||
12 | + */ | ||
13 | + void addVertex(V vertex); | ||
14 | + | ||
15 | + /** | ||
16 | + * Removes the specified vertex from the graph. | ||
17 | + * | ||
18 | + * @param vertex vertex to be removed | ||
19 | + */ | ||
20 | + void removeVertex(V vertex); | ||
21 | + | ||
22 | + /** | ||
23 | + * Adds the specified edge to this graph. If the edge vertexes are not | ||
24 | + * already in the graph, they will be added as well. | ||
25 | + * | ||
26 | + * @param edge new edge | ||
27 | + */ | ||
28 | + void addEdge(E edge); | ||
29 | + | ||
30 | + /** | ||
31 | + * Removes the specified edge from the graph. | ||
32 | + * | ||
33 | + * @param edge edge to be removed | ||
34 | + */ | ||
35 | + void removeEdge(E edge); | ||
36 | + | ||
37 | + /** | ||
38 | + * Returns an immutable copy of this graph. | ||
39 | + * | ||
40 | + * @return immutable copy | ||
41 | + */ | ||
42 | + Graph<V, E> toImmutable(); | ||
43 | + | ||
44 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Abstraction of a mutable path that allows gradual construction. | ||
5 | + */ | ||
6 | +public interface MutablePath<V extends Vertex, E extends Edge<V>> extends Path<V, E> { | ||
7 | + | ||
8 | + /** | ||
9 | + * Inserts a new edge at the beginning of this path. The edge must be | ||
10 | + * adjacent to the prior start of the path. | ||
11 | + * | ||
12 | + * @param edge edge to be inserted | ||
13 | + */ | ||
14 | + void insertEdge(E edge); | ||
15 | + | ||
16 | + /** | ||
17 | + * Appends a new edge at the end of the this path. The edge must be | ||
18 | + * adjacent to the prior end of the path. | ||
19 | + * | ||
20 | + * @param edge edge to be inserted | ||
21 | + */ | ||
22 | + void appendEdge(E edge); | ||
23 | + | ||
24 | + /** | ||
25 | + * Removes the specified edge. This edge must be either at the start or | ||
26 | + * at the end of the path, or it must be a cyclic edge in order not to | ||
27 | + * violate the contiguous path property. | ||
28 | + * | ||
29 | + * @param edge edge to be removed | ||
30 | + */ | ||
31 | + void removeEdge(E edge); | ||
32 | + | ||
33 | + /** | ||
34 | + * Sets the total path cost as a unit-less double. | ||
35 | + * | ||
36 | + * @param cost new path cost | ||
37 | + */ | ||
38 | + void setCost(double cost); | ||
39 | + | ||
40 | + /** | ||
41 | + * Returns an immutable copy of this path. | ||
42 | + * | ||
43 | + * @return immutable copy | ||
44 | + */ | ||
45 | + Path<V, E> toImmutable(); | ||
46 | + | ||
47 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.List; | ||
4 | + | ||
5 | +/** | ||
6 | + * Representation of a path in a graph as a sequence of edges. Paths are | ||
7 | + * assumed to be continuous, where adjacent edges must share a vertex. | ||
8 | + * | ||
9 | + * @param <V> vertex type | ||
10 | + * @param <E> edge type | ||
11 | + */ | ||
12 | +public interface Path<V extends Vertex, E extends Edge<V>> extends Edge<V> { | ||
13 | + | ||
14 | + /** | ||
15 | + * Returns the list of edges comprising the path. Adjacent edges will | ||
16 | + * share the same vertex, meaning that a source of one edge, will be the | ||
17 | + * same as the destination of the prior edge. | ||
18 | + * | ||
19 | + * @return list of path edges | ||
20 | + */ | ||
21 | + List<E> edges(); | ||
22 | + | ||
23 | + /** | ||
24 | + * Returns the total cost of the path as a unit-less number. | ||
25 | + * | ||
26 | + * @return path cost as a unit-less number | ||
27 | + */ | ||
28 | + double cost(); | ||
29 | + | ||
30 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.testing.EqualsTester; | ||
4 | +import org.junit.Test; | ||
5 | + | ||
6 | +/** | ||
7 | + * Test of the base edge implementation. | ||
8 | + */ | ||
9 | +public class AbstractEdgeTest { | ||
10 | + | ||
11 | + @Test | ||
12 | + public void equality() { | ||
13 | + TestVertex v1 = new TestVertex("1"); | ||
14 | + TestVertex v2 = new TestVertex("2"); | ||
15 | + new EqualsTester() | ||
16 | + .addEqualityGroup(new TestEdge(v1, v2, 1), | ||
17 | + new TestEdge(v1, v2, 1)) | ||
18 | + .addEqualityGroup(new TestEdge(v2, v1, 1)) | ||
19 | + .testEquals(); | ||
20 | + } | ||
21 | + | ||
22 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import static com.google.common.collect.ImmutableSet.of; | ||
6 | +import static org.junit.Assert.assertEquals; | ||
7 | + | ||
8 | +/** | ||
9 | + * Base for all graph search tests. | ||
10 | + */ | ||
11 | +public abstract class AbstractGraphPathSearchTest extends GraphTest { | ||
12 | + | ||
13 | + /** | ||
14 | + * Creates a test-specific graph search to exercise. | ||
15 | + * | ||
16 | + * @return graph search | ||
17 | + */ | ||
18 | + protected abstract AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch(); | ||
19 | + | ||
20 | + @Test(expected = IllegalArgumentException.class) | ||
21 | + public void noSuchSourceArgument() { | ||
22 | + graphSearch().search(new AdjacencyListsGraph<>(of(B, C), | ||
23 | + of(new TestEdge(B, C, 1))), | ||
24 | + A, H, weight); | ||
25 | + } | ||
26 | + | ||
27 | + @Test(expected = NullPointerException.class) | ||
28 | + public void nullGraphArgument() { | ||
29 | + graphSearch().search(null, A, H, weight); | ||
30 | + } | ||
31 | + | ||
32 | + @Test(expected = NullPointerException.class) | ||
33 | + public void nullSourceArgument() { | ||
34 | + graphSearch().search(new AdjacencyListsGraph<>(of(B, C), | ||
35 | + of(new TestEdge(B, C, 1))), | ||
36 | + null, H, weight); | ||
37 | + } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void samenessThreshold() { | ||
41 | + AbstractGraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | ||
42 | + search.setSamenessThreshold(0.3); | ||
43 | + assertEquals("incorrect threshold", 0.3, search.samenessThreshold(), 0.01); | ||
44 | + } | ||
45 | + | ||
46 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.ImmutableSet; | ||
4 | +import com.google.common.testing.EqualsTester; | ||
5 | +import org.junit.Test; | ||
6 | + | ||
7 | +import java.util.Set; | ||
8 | + | ||
9 | +import static org.junit.Assert.assertEquals; | ||
10 | + | ||
11 | +/** | ||
12 | + * Tests of the graph implementation. | ||
13 | + */ | ||
14 | +public class AdjacencyListsGraphTest { | ||
15 | + | ||
16 | + private static final TestVertex A = new TestVertex("A"); | ||
17 | + private static final TestVertex B = new TestVertex("B"); | ||
18 | + private static final TestVertex C = new TestVertex("C"); | ||
19 | + private static final TestVertex D = new TestVertex("D"); | ||
20 | + private static final TestVertex E = new TestVertex("E"); | ||
21 | + private static final TestVertex F = new TestVertex("F"); | ||
22 | + private static final TestVertex G = new TestVertex("G"); | ||
23 | + | ||
24 | + private final Set<TestEdge> edges = | ||
25 | + ImmutableSet.of(new TestEdge(A, B, 1), new TestEdge(B, C, 1), | ||
26 | + new TestEdge(C, D, 1), new TestEdge(D, A, 1), | ||
27 | + new TestEdge(B, D, 1)); | ||
28 | + | ||
29 | + @Test | ||
30 | + public void equality() { | ||
31 | + Set<TestVertex> vertexes = ImmutableSet.of(A, B, C, D, E, F); | ||
32 | + Set<TestVertex> vertexes2 = ImmutableSet.of(A, B, C, D, E, F, G); | ||
33 | + | ||
34 | + AdjacencyListsGraph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(vertexes, edges); | ||
35 | + AdjacencyListsGraph<TestVertex, TestEdge> same = new AdjacencyListsGraph<>(vertexes, edges); | ||
36 | + AdjacencyListsGraph<TestVertex, TestEdge> different = new AdjacencyListsGraph<>(vertexes2, edges); | ||
37 | + | ||
38 | + new EqualsTester() | ||
39 | + .addEqualityGroup(graph, same) | ||
40 | + .addEqualityGroup(different) | ||
41 | + .testEquals(); | ||
42 | + } | ||
43 | + | ||
44 | + @Test | ||
45 | + public void basics() { | ||
46 | + Set<TestVertex> vertexes = ImmutableSet.of(A, B, C, D, E, F); | ||
47 | + AdjacencyListsGraph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(vertexes, edges); | ||
48 | + assertEquals("incorrect vertex count", 6, graph.getVertexes().size()); | ||
49 | + assertEquals("incorrect edge count", 5, graph.getEdges().size()); | ||
50 | + | ||
51 | + assertEquals("incorrect egress edge count", 1, graph.getEdgesFrom(A).size()); | ||
52 | + assertEquals("incorrect ingress edge count", 1, graph.getEdgesTo(A).size()); | ||
53 | + assertEquals("incorrect ingress edge count", 1, graph.getEdgesTo(C).size()); | ||
54 | + assertEquals("incorrect egress edge count", 2, graph.getEdgesFrom(B).size()); | ||
55 | + assertEquals("incorrect ingress edge count", 2, graph.getEdgesTo(D).size()); | ||
56 | + } | ||
57 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import java.util.HashSet; | ||
6 | +import java.util.Set; | ||
7 | + | ||
8 | +import static org.junit.Assert.assertEquals; | ||
9 | + | ||
10 | +/** | ||
11 | + * Test of the Bellman-Ford algorithm. | ||
12 | + */ | ||
13 | +public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ||
14 | + | ||
15 | + @Override | ||
16 | + protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() { | ||
17 | + return new BellmanFordGraphSearch<>(); | ||
18 | + } | ||
19 | + | ||
20 | + @Test | ||
21 | + @Override | ||
22 | + public void defaultGraphTest() { | ||
23 | + executeDefaultTest(7, 5, 5.0); | ||
24 | + } | ||
25 | + | ||
26 | + @Test | ||
27 | + public void defaultHopCountWeight() { | ||
28 | + weight = null; | ||
29 | + executeDefaultTest(10, 3, 3.0); | ||
30 | + } | ||
31 | + | ||
32 | + @Test | ||
33 | + public void searchGraphWithNegativeCycles() { | ||
34 | + Set<TestVertex> vertexes = new HashSet<>(vertices()); | ||
35 | + vertexes.add(Z); | ||
36 | + | ||
37 | + Set<TestEdge> edges = new HashSet<>(edges()); | ||
38 | + edges.add(new TestEdge(G, Z, 1.0)); | ||
39 | + edges.add(new TestEdge(Z, G, -2.0)); | ||
40 | + | ||
41 | + g = new AdjacencyListsGraph<>(vertexes, edges); | ||
42 | + | ||
43 | + GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | ||
44 | + Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | ||
45 | + assertEquals("incorrect paths count", 1, paths.size()); | ||
46 | + | ||
47 | + Path p = paths.iterator().next(); | ||
48 | + assertEquals("incorrect src", A, p.src()); | ||
49 | + assertEquals("incorrect dst", H, p.dst()); | ||
50 | + assertEquals("incorrect path length", 5, p.edges().size()); | ||
51 | + assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); | ||
52 | + | ||
53 | + paths = search.search(g, A, G, weight).paths(); | ||
54 | + assertEquals("incorrect paths count", 0, paths.size()); | ||
55 | + | ||
56 | + paths = search.search(g, A, null, weight).paths(); | ||
57 | + printPaths(paths); | ||
58 | + assertEquals("incorrect paths count", 6, paths.size()); | ||
59 | + } | ||
60 | + | ||
61 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import java.util.Set; | ||
6 | + | ||
7 | +import static org.junit.Assert.assertEquals; | ||
8 | + | ||
9 | +/** | ||
10 | + * Test of the BFS and similar path search algorithms. | ||
11 | + */ | ||
12 | +public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { | ||
13 | + | ||
14 | + @Override | ||
15 | + protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() { | ||
16 | + return new BreadthFirstSearch<>(); | ||
17 | + } | ||
18 | + | ||
19 | + @Test | ||
20 | + public void defaultGraphTest() { | ||
21 | + executeDefaultTest(7, 3, 8.0); | ||
22 | + } | ||
23 | + | ||
24 | + @Test | ||
25 | + public void defaultHopCountWeight() { | ||
26 | + weight = null; | ||
27 | + executeDefaultTest(7, 3, 3.0); | ||
28 | + } | ||
29 | + | ||
30 | + // Executes the default test | ||
31 | + protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) { | ||
32 | + g = new AdjacencyListsGraph<>(vertices(), edges()); | ||
33 | + | ||
34 | + GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | ||
35 | + Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | ||
36 | + assertEquals("incorrect paths count", 1, paths.size()); | ||
37 | + | ||
38 | + Path p = paths.iterator().next(); | ||
39 | + assertEquals("incorrect src", A, p.src()); | ||
40 | + assertEquals("incorrect dst", H, p.dst()); | ||
41 | + assertEquals("incorrect path length", pathLength, p.edges().size()); | ||
42 | + assertEquals("incorrect path cost", pathCost, p.cost(), 0.1); | ||
43 | + | ||
44 | + paths = search.search(g, A, null, weight).paths(); | ||
45 | + printPaths(paths); | ||
46 | + assertEquals("incorrect paths count", pathCount, paths.size()); | ||
47 | + } | ||
48 | + | ||
49 | + // Executes the search and validates its results. | ||
50 | + protected void executeSearch(GraphPathSearch<TestVertex, TestEdge> search, | ||
51 | + Graph<TestVertex, TestEdge> graph, | ||
52 | + TestVertex src, TestVertex dst, | ||
53 | + EdgeWeight<TestVertex, TestEdge> weight, | ||
54 | + int pathCount, double pathCost) { | ||
55 | + GraphPathSearch.Result<TestVertex, TestEdge> result = | ||
56 | + search.search(graph, src, dst, weight); | ||
57 | + Set<Path<TestVertex, TestEdge>> paths = result.paths(); | ||
58 | + printPaths(paths); | ||
59 | + assertEquals("incorrect paths count", pathCount, paths.size()); | ||
60 | + if (pathCount > 0) { | ||
61 | + Path<TestVertex, TestEdge> path = paths.iterator().next(); | ||
62 | + assertEquals("incorrect path cost", pathCost, path.cost(), 0.1); | ||
63 | + } | ||
64 | + } | ||
65 | + | ||
66 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.testing.EqualsTester; | ||
4 | +import org.junit.Test; | ||
5 | + | ||
6 | +import static com.google.common.collect.ImmutableList.of; | ||
7 | +import static org.junit.Assert.assertEquals; | ||
8 | +import static org.junit.Assert.assertNull; | ||
9 | + | ||
10 | +/** | ||
11 | + * Test of the default mutable path. | ||
12 | + */ | ||
13 | +public class DefaultMutablePathTest extends DefaultPathTest { | ||
14 | + | ||
15 | + @Test | ||
16 | + public void equality() { | ||
17 | + DefaultPath<TestVertex, TestEdge> p1 = | ||
18 | + new DefaultPath<>(of(new TestEdge(A, B, 1), | ||
19 | + new TestEdge(B, C, 1)), 2.0); | ||
20 | + DefaultPath<TestVertex, TestEdge> p2 = | ||
21 | + new DefaultPath<>(of(new TestEdge(A, B, 1), | ||
22 | + new TestEdge(B, D, 1)), 2.0); | ||
23 | + new EqualsTester().addEqualityGroup(new DefaultMutablePath<>(p1), | ||
24 | + new DefaultMutablePath<>(p1)) | ||
25 | + .addEqualityGroup(new DefaultMutablePath<>(p2)) | ||
26 | + .testEquals(); | ||
27 | + } | ||
28 | + | ||
29 | + @Test | ||
30 | + public void empty() { | ||
31 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
32 | + assertNull("src should be null", p.src()); | ||
33 | + assertNull("dst should be null", p.dst()); | ||
34 | + assertEquals("incorrect edge count", 0, p.edges().size()); | ||
35 | + assertEquals("incorrect path cost", 0.0, p.cost(), 0.1); | ||
36 | + } | ||
37 | + | ||
38 | + @Test | ||
39 | + public void pathCost() { | ||
40 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
41 | + p.setCost(4); | ||
42 | + assertEquals("incorrect path cost", 4.0, p.cost(), 0.1); | ||
43 | + } | ||
44 | + | ||
45 | + private void validatePath(Path<TestVertex, TestEdge> p, | ||
46 | + TestVertex src, TestVertex dst, int length) { | ||
47 | + validatePath(p, src, dst, length, 0.0); | ||
48 | + } | ||
49 | + | ||
50 | + @Test | ||
51 | + public void insertEdge() { | ||
52 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
53 | + p.insertEdge(new TestEdge(B, C, 1)); | ||
54 | + p.insertEdge(new TestEdge(A, B, 1)); | ||
55 | + validatePath(p, A, C, 2); | ||
56 | + } | ||
57 | + | ||
58 | + @Test | ||
59 | + public void appendEdge() { | ||
60 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
61 | + p.appendEdge(new TestEdge(A, B, 1)); | ||
62 | + p.appendEdge(new TestEdge(B, C, 1)); | ||
63 | + validatePath(p, A, C, 2); | ||
64 | + } | ||
65 | + | ||
66 | + @Test | ||
67 | + public void removeEdge() { | ||
68 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
69 | + p.appendEdge(new TestEdge(A, B, 1)); | ||
70 | + p.appendEdge(new TestEdge(B, C, 1)); | ||
71 | + p.appendEdge(new TestEdge(C, C, 2)); | ||
72 | + p.appendEdge(new TestEdge(C, D, 1)); | ||
73 | + validatePath(p, A, D, 4); | ||
74 | + | ||
75 | + p.removeEdge(new TestEdge(A, B, 1)); | ||
76 | + validatePath(p, B, D, 3); | ||
77 | + | ||
78 | + p.removeEdge(new TestEdge(C, C, 2)); | ||
79 | + validatePath(p, B, D, 2); | ||
80 | + | ||
81 | + p.removeEdge(new TestEdge(C, D, 1)); | ||
82 | + validatePath(p, B, C, 1); | ||
83 | + } | ||
84 | + | ||
85 | + @Test | ||
86 | + public void toImmutable() { | ||
87 | + MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>(); | ||
88 | + p.appendEdge(new TestEdge(A, B, 1)); | ||
89 | + p.appendEdge(new TestEdge(B, C, 1)); | ||
90 | + validatePath(p, A, C, 2); | ||
91 | + | ||
92 | + assertEquals("immutables should equal", p.toImmutable(), p.toImmutable()); | ||
93 | + validatePath(p.toImmutable(), A, C, 2); | ||
94 | + } | ||
95 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.testing.EqualsTester; | ||
4 | +import org.junit.Test; | ||
5 | + | ||
6 | +import java.util.List; | ||
7 | + | ||
8 | +import static com.google.common.collect.ImmutableList.of; | ||
9 | +import static org.junit.Assert.assertEquals; | ||
10 | + | ||
11 | +/** | ||
12 | + * Test of the default path. | ||
13 | + */ | ||
14 | +public class DefaultPathTest extends GraphTest { | ||
15 | + | ||
16 | + @Test | ||
17 | + public void equality() { | ||
18 | + List<TestEdge> edges = of(new TestEdge(A, B, 1), new TestEdge(B, C, 1)); | ||
19 | + new EqualsTester().addEqualityGroup(new DefaultPath<>(edges, 2.0), | ||
20 | + new DefaultPath<>(edges, 2.0)) | ||
21 | + .addEqualityGroup(new DefaultPath<>(edges, 3.0)) | ||
22 | + .testEquals(); | ||
23 | + } | ||
24 | + | ||
25 | + @Test | ||
26 | + public void basics() { | ||
27 | + Path<TestVertex, TestEdge> p = new DefaultPath<>(of(new TestEdge(A, B, 1), | ||
28 | + new TestEdge(B, C, 1)), 2.0); | ||
29 | + validatePath(p, A, C, 2, 2.0); | ||
30 | + } | ||
31 | + | ||
32 | + // Validates the path against expected attributes | ||
33 | + protected void validatePath(Path<TestVertex, TestEdge> p, | ||
34 | + TestVertex src, TestVertex dst, | ||
35 | + int length, double cost) { | ||
36 | + assertEquals("incorrect path length", length, p.edges().size()); | ||
37 | + assertEquals("incorrect source", src, p.src()); | ||
38 | + assertEquals("incorrect destination", dst, p.dst()); | ||
39 | + assertEquals("incorrect path cost", cost, p.cost(), 0.1); | ||
40 | + } | ||
41 | + | ||
42 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import java.util.Set; | ||
6 | + | ||
7 | +import static org.junit.Assert.assertEquals; | ||
8 | +import static org.junit.Assert.assertTrue; | ||
9 | +import static org.onlab.graph.DepthFirstSearch.EdgeType; | ||
10 | + | ||
11 | +/** | ||
12 | + * Test of the DFS algorithm. | ||
13 | + */ | ||
14 | +public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { | ||
15 | + | ||
16 | + @Override | ||
17 | + protected DepthFirstSearch<TestVertex, TestEdge> graphSearch() { | ||
18 | + return new DepthFirstSearch<>(); | ||
19 | + } | ||
20 | + | ||
21 | + @Test | ||
22 | + public void defaultGraphTest() { | ||
23 | + executeDefaultTest(3, 6, 5.0, 12.0); | ||
24 | + executeBroadSearch(); | ||
25 | + } | ||
26 | + | ||
27 | + @Test | ||
28 | + public void defaultHopCountWeight() { | ||
29 | + weight = null; | ||
30 | + executeDefaultTest(3, 6, 3.0, 6.0); | ||
31 | + executeBroadSearch(); | ||
32 | + } | ||
33 | + | ||
34 | + protected void executeDefaultTest(int minLength, int maxLength, | ||
35 | + double minCost, double maxCost) { | ||
36 | + g = new AdjacencyListsGraph<>(vertices(), edges()); | ||
37 | + DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | ||
38 | + | ||
39 | + DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | ||
40 | + search.search(g, A, H, weight); | ||
41 | + Set<Path<TestVertex, TestEdge>> paths = result.paths(); | ||
42 | + assertEquals("incorrect path count", 1, paths.size()); | ||
43 | + | ||
44 | + Path path = paths.iterator().next(); | ||
45 | + System.out.println(path); | ||
46 | + assertEquals("incorrect src", A, path.src()); | ||
47 | + assertEquals("incorrect dst", H, path.dst()); | ||
48 | + | ||
49 | + int l = path.edges().size(); | ||
50 | + assertTrue("incorrect path length " + l, | ||
51 | + minLength <= l && l <= maxLength); | ||
52 | + assertTrue("incorrect path cost " + path.cost(), | ||
53 | + minCost <= path.cost() && path.cost() <= maxCost); | ||
54 | + | ||
55 | + System.out.println(result.edges()); | ||
56 | + printPaths(paths); | ||
57 | + } | ||
58 | + | ||
59 | + public void executeBroadSearch() { | ||
60 | + g = new AdjacencyListsGraph<>(vertices(), edges()); | ||
61 | + DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | ||
62 | + | ||
63 | + // Perform narrow path search to a specific destination. | ||
64 | + DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | ||
65 | + search.search(g, A, null, weight); | ||
66 | + assertEquals("incorrect paths count", 7, result.paths().size()); | ||
67 | + | ||
68 | + int[] types = new int[]{0, 0, 0, 0}; | ||
69 | + for (EdgeType t : result.edges().values()) { | ||
70 | + types[t.ordinal()] += 1; | ||
71 | + } | ||
72 | + assertEquals("incorrect tree-edge count", 7, | ||
73 | + types[EdgeType.TREE_EDGE.ordinal()]); | ||
74 | + assertEquals("incorrect back-edge count", 1, | ||
75 | + types[EdgeType.BACK_EDGE.ordinal()]); | ||
76 | + assertEquals("incorrect cross-edge & forward-edge count", 4, | ||
77 | + types[EdgeType.FORWARD_EDGE.ordinal()] + | ||
78 | + types[EdgeType.CROSS_EDGE.ordinal()]); | ||
79 | + } | ||
80 | + | ||
81 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import java.util.Set; | ||
6 | + | ||
7 | +import static com.google.common.collect.ImmutableSet.of; | ||
8 | +import static org.junit.Assert.assertEquals; | ||
9 | + | ||
10 | +/** | ||
11 | + * Test of the Dijkstra algorithm. | ||
12 | + */ | ||
13 | +public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { | ||
14 | + | ||
15 | + @Override | ||
16 | + protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() { | ||
17 | + return new DijkstraGraphSearch<>(); | ||
18 | + } | ||
19 | + | ||
20 | + @Test | ||
21 | + @Override | ||
22 | + public void defaultGraphTest() { | ||
23 | + executeDefaultTest(7, 5, 5.0); | ||
24 | + } | ||
25 | + | ||
26 | + @Test | ||
27 | + @Override | ||
28 | + public void defaultHopCountWeight() { | ||
29 | + weight = null; | ||
30 | + executeDefaultTest(10, 3, 3.0); | ||
31 | + } | ||
32 | + | ||
33 | + @Test | ||
34 | + public void noPath() { | ||
35 | + g = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
36 | + of(new TestEdge(A, B, 1), | ||
37 | + new TestEdge(B, A, 1), | ||
38 | + new TestEdge(C, D, 1), | ||
39 | + new TestEdge(D, C, 1))); | ||
40 | + GraphPathSearch<TestVertex, TestEdge> gs = graphSearch(); | ||
41 | + Set<Path<TestVertex, TestEdge>> paths = gs.search(g, A, B, weight).paths(); | ||
42 | + printPaths(paths); | ||
43 | + assertEquals("incorrect paths count", 1, paths.size()); | ||
44 | + assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | ||
45 | + | ||
46 | + paths = gs.search(g, A, D, weight).paths(); | ||
47 | + printPaths(paths); | ||
48 | + assertEquals("incorrect paths count", 0, paths.size()); | ||
49 | + | ||
50 | + paths = gs.search(g, A, null, weight).paths(); | ||
51 | + printPaths(paths); | ||
52 | + assertEquals("incorrect paths count", 1, paths.size()); | ||
53 | + assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | ||
54 | + } | ||
55 | + | ||
56 | + @Test | ||
57 | + public void simpleMultiplePath() { | ||
58 | + g = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
59 | + of(new TestEdge(A, B, 1), | ||
60 | + new TestEdge(A, C, 1), | ||
61 | + new TestEdge(B, D, 1), | ||
62 | + new TestEdge(C, D, 1))); | ||
63 | + executeSearch(graphSearch(), g, A, D, weight, 2, 2.0); | ||
64 | + } | ||
65 | + | ||
66 | + @Test | ||
67 | + public void denseMultiplePath() { | ||
68 | + g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G), | ||
69 | + of(new TestEdge(A, B, 1), | ||
70 | + new TestEdge(A, C, 1), | ||
71 | + new TestEdge(B, D, 1), | ||
72 | + new TestEdge(C, D, 1), | ||
73 | + new TestEdge(D, E, 1), | ||
74 | + new TestEdge(D, F, 1), | ||
75 | + new TestEdge(E, G, 1), | ||
76 | + new TestEdge(F, G, 1), | ||
77 | + new TestEdge(A, G, 4))); | ||
78 | + executeSearch(graphSearch(), g, A, G, weight, 5, 4.0); | ||
79 | + } | ||
80 | + | ||
81 | + @Test | ||
82 | + public void dualEdgeMultiplePath() { | ||
83 | + g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), | ||
84 | + of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), | ||
85 | + new TestEdge(B, D, 2), new TestEdge(B, C, 1), | ||
86 | + new TestEdge(B, E, 4), new TestEdge(C, E, 1), | ||
87 | + new TestEdge(D, H, 5), new TestEdge(D, E, 1), | ||
88 | + new TestEdge(E, F, 1), new TestEdge(F, D, 1), | ||
89 | + new TestEdge(F, G, 1), new TestEdge(F, H, 1), | ||
90 | + new TestEdge(A, E, 3), new TestEdge(B, D, 1))); | ||
91 | + executeSearch(graphSearch(), g, A, E, weight, 3, 3.0); | ||
92 | + } | ||
93 | + | ||
94 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.Set; | ||
4 | + | ||
5 | +import static com.google.common.collect.ImmutableSet.of; | ||
6 | + | ||
7 | +/** | ||
8 | + * Base class for various graph-related tests. | ||
9 | + */ | ||
10 | +public class GraphTest { | ||
11 | + | ||
12 | + static final TestVertex A = new TestVertex("A"); | ||
13 | + static final TestVertex B = new TestVertex("B"); | ||
14 | + static final TestVertex C = new TestVertex("C"); | ||
15 | + static final TestVertex D = new TestVertex("D"); | ||
16 | + static final TestVertex E = new TestVertex("E"); | ||
17 | + static final TestVertex F = new TestVertex("F"); | ||
18 | + static final TestVertex G = new TestVertex("G"); | ||
19 | + static final TestVertex H = new TestVertex("H"); | ||
20 | + static final TestVertex Z = new TestVertex("Z"); | ||
21 | + | ||
22 | + protected Graph<TestVertex, TestEdge> g; | ||
23 | + | ||
24 | + protected EdgeWeight<TestVertex, TestEdge> weight = | ||
25 | + new EdgeWeight<TestVertex, TestEdge>() { | ||
26 | + @Override | ||
27 | + public double weight(TestEdge edge) { | ||
28 | + return edge.weight(); | ||
29 | + } | ||
30 | + }; | ||
31 | + | ||
32 | + protected void printPaths(Set<Path<TestVertex, TestEdge>> paths) { | ||
33 | + for (Path p : paths) { | ||
34 | + System.out.println(p); | ||
35 | + } | ||
36 | + } | ||
37 | + | ||
38 | + protected Set<TestVertex> vertices() { | ||
39 | + return of(A, B, C, D, E, F, G, H); | ||
40 | + } | ||
41 | + | ||
42 | + protected Set<TestEdge> edges() { | ||
43 | + return of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), | ||
44 | + new TestEdge(B, D, 2), new TestEdge(B, C, 1), | ||
45 | + new TestEdge(B, E, 4), new TestEdge(C, E, 1), | ||
46 | + new TestEdge(D, H, 5), new TestEdge(D, E, 1), | ||
47 | + new TestEdge(E, F, 1), new TestEdge(F, D, 1), | ||
48 | + new TestEdge(F, G, 1), new TestEdge(F, H, 1)); | ||
49 | + } | ||
50 | + | ||
51 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import com.google.common.collect.Ordering; | ||
4 | +import com.google.common.testing.EqualsTester; | ||
5 | +import org.junit.Test; | ||
6 | + | ||
7 | +import java.util.ArrayList; | ||
8 | +import java.util.Comparator; | ||
9 | + | ||
10 | +import static com.google.common.collect.ImmutableList.of; | ||
11 | +import static org.junit.Assert.*; | ||
12 | + | ||
13 | +/** | ||
14 | + * Heap data structure tests. | ||
15 | + */ | ||
16 | +public class HeapTest { | ||
17 | + | ||
18 | + private ArrayList<Integer> data = | ||
19 | + new ArrayList<>(of(6, 4, 5, 9, 8, 3, 2, 1, 7, 0)); | ||
20 | + | ||
21 | + private static final Comparator<Integer> MIN = Ordering.natural().reverse(); | ||
22 | + private static final Comparator<Integer> MAX = Ordering.natural(); | ||
23 | + | ||
24 | + @Test | ||
25 | + public void equality() { | ||
26 | + new EqualsTester() | ||
27 | + .addEqualityGroup(new Heap<>(data, MIN), | ||
28 | + new Heap<>(data, MIN)) | ||
29 | + .addEqualityGroup(new Heap<>(data, MAX)) | ||
30 | + .testEquals(); | ||
31 | + } | ||
32 | + | ||
33 | + @Test | ||
34 | + public void empty() { | ||
35 | + Heap<Integer> h = new Heap<>(new ArrayList<Integer>(), MIN); | ||
36 | + assertTrue("should be empty", h.isEmpty()); | ||
37 | + assertEquals("incorrect size", 0, h.size()); | ||
38 | + assertNull("no item expected", h.extreme()); | ||
39 | + assertNull("no item expected", h.extractExtreme()); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void insert() { | ||
44 | + Heap<Integer> h = new Heap<>(data, MIN); | ||
45 | + assertEquals("incorrect size", 10, h.size()); | ||
46 | + h.insert(3); | ||
47 | + assertEquals("incorrect size", 11, h.size()); | ||
48 | + } | ||
49 | + | ||
50 | + @Test | ||
51 | + public void minQueue() { | ||
52 | + Heap<Integer> h = new Heap<>(data, MIN); | ||
53 | + assertFalse("should not be empty", h.isEmpty()); | ||
54 | + assertEquals("incorrect size", 10, h.size()); | ||
55 | + assertEquals("incorrect extreme", (Integer) 0, h.extreme()); | ||
56 | + | ||
57 | + for (int i = 0, n = h.size(); i < n; i++) { | ||
58 | + assertEquals("incorrect element", (Integer) i, h.extractExtreme()); | ||
59 | + } | ||
60 | + assertTrue("should be empty", h.isEmpty()); | ||
61 | + } | ||
62 | + | ||
63 | + @Test | ||
64 | + public void maxQueue() { | ||
65 | + Heap<Integer> h = new Heap<>(data, MAX); | ||
66 | + assertFalse("should not be empty", h.isEmpty()); | ||
67 | + assertEquals("incorrect size", 10, h.size()); | ||
68 | + assertEquals("incorrect extreme", (Integer) 9, h.extreme()); | ||
69 | + | ||
70 | + for (int i = h.size(); i > 0; i--) { | ||
71 | + assertEquals("incorrect element", (Integer) (i - 1), h.extractExtreme()); | ||
72 | + } | ||
73 | + assertTrue("should be empty", h.isEmpty()); | ||
74 | + } | ||
75 | + | ||
76 | + @Test | ||
77 | + public void iterator() { | ||
78 | + Heap<Integer> h = new Heap<>(data, MIN); | ||
79 | + assertTrue("should have next element", h.iterator().hasNext()); | ||
80 | + } | ||
81 | + | ||
82 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import static com.google.common.base.Objects.toStringHelper; | ||
6 | + | ||
7 | +/** | ||
8 | + * Test edge. | ||
9 | + */ | ||
10 | +public class TestEdge extends AbstractEdge<TestVertex> { | ||
11 | + | ||
12 | + private final double weight; | ||
13 | + | ||
14 | + /** | ||
15 | + * Creates a new edge between the specified source and destination vertexes. | ||
16 | + * | ||
17 | + * @param src source vertex | ||
18 | + * @param dst destination vertex | ||
19 | + * @param weight edge weight | ||
20 | + */ | ||
21 | + public TestEdge(TestVertex src, TestVertex dst, double weight) { | ||
22 | + super(src, dst); | ||
23 | + this.weight = weight; | ||
24 | + } | ||
25 | + | ||
26 | + /** | ||
27 | + * Returns the edge weight. | ||
28 | + * | ||
29 | + * @return edge weight | ||
30 | + */ | ||
31 | + public double weight() { | ||
32 | + return weight; | ||
33 | + } | ||
34 | + | ||
35 | + @Override | ||
36 | + public int hashCode() { | ||
37 | + return 31 * super.hashCode() + Objects.hash(weight); | ||
38 | + } | ||
39 | + | ||
40 | + @Override | ||
41 | + public boolean equals(Object obj) { | ||
42 | + if (obj instanceof TestEdge) { | ||
43 | + final TestEdge other = (TestEdge) obj; | ||
44 | + return super.equals(obj) && Objects.equals(this.weight, other.weight); | ||
45 | + } | ||
46 | + return false; | ||
47 | + } | ||
48 | + | ||
49 | + @Override | ||
50 | + public String toString() { | ||
51 | + return toStringHelper(this).add("src", src()).add("dst", dst()). | ||
52 | + add("weight", weight).toString(); | ||
53 | + } | ||
54 | + | ||
55 | +} |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +/** | ||
6 | + * Test vertex. | ||
7 | + */ | ||
8 | +public class TestVertex implements Vertex { | ||
9 | + | ||
10 | + private final String name; | ||
11 | + | ||
12 | + public TestVertex(String name) { | ||
13 | + this.name = name; | ||
14 | + } | ||
15 | + | ||
16 | + @Override | ||
17 | + public int hashCode() { | ||
18 | + return Objects.hash(name); | ||
19 | + } | ||
20 | + | ||
21 | + @Override | ||
22 | + public boolean equals(Object obj) { | ||
23 | + if (obj instanceof TestVertex) { | ||
24 | + final TestVertex other = (TestVertex) obj; | ||
25 | + return Objects.equals(this.name, other.name); | ||
26 | + } | ||
27 | + return false; | ||
28 | + } | ||
29 | + | ||
30 | + @Override | ||
31 | + public String toString() { | ||
32 | + return name; | ||
33 | + } | ||
34 | + | ||
35 | +} |
-
Please register or login to post a comment