/*
* Copyright 2011 David Jurgens
*
* This file is part of the S-Space package and is covered under the terms and
* conditions therein.
*
* The S-Space package is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation and distributed hereunder to you.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
* EXPRESS OR IMPLIED ARE MADE. BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
* NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
* PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
* WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
* RIGHTS.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package edu.ucla.sspace.graph;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import edu.ucla.sspace.util.CombinedIterator;
import edu.ucla.sspace.util.CombinedSet;
import edu.ucla.sspace.util.DisjointSets;
import edu.ucla.sspace.util.SetDecorator;
import edu.ucla.sspace.util.primitive.AbstractIntSet;
import edu.ucla.sspace.util.primitive.IntIterator;
import edu.ucla.sspace.util.primitive.IntSet;
import edu.ucla.sspace.util.primitive.PrimitiveCollections;
import edu.ucla.sspace.util.primitive.TroveIntSet;
import gnu.trove.TDecorators;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
/**
* A {@link WeightedGraph weighted}, {@link DirectedGraph directed} {@link
* Multigraph} implementation.
*
* @param T a class type whose values are used to distinguish between edge types
*
* @author David Jurgens
*/
public class WeightedDirectedMultigraph<T>
implements Multigraph<T,WeightedDirectedTypedEdge<T>>,
DirectedGraph<WeightedDirectedTypedEdge<T>>,
WeightedGraph<WeightedDirectedTypedEdge<T>>,
java.io.Serializable {
private static final long serialVersionUID = 1L;
/**
* The count of the type distribution for all edges in the graph.
*/
private final TObjectIntMap<T> typeCounts;
/**
* The set of vertices in this mutligraph. This set is maintained
* independently of the vertex sets in the type-specific edge graphs to
* provide a fast, type independent state of which vertices are in the
* graph.
*/
private final TIntObjectMap<SparseWeightedDirectedTypedEdgeSet<T>> vertexToEdges;
/**
* A collection of all the subgraphs that have been returned from this
* graph. This collection is necessary to inform subgraphs of any vertex
* changes (removals) to this graph without requiring them to constantly
* check for updates. We use a {@link WeakReference} in order to keep track
* of the canonical {@code Subgraph} instance while ensuring that it is
* garbage collected when it is no longer referred to (which would never
* happen if this list contained strong references).
*/
private Collection<WeakReference<Subgraph>> subgraphs;
/**
* The number of edges contained in this graph.
*/
private int size;
/**
* Creates an empty graph with node edges
*/
public WeightedDirectedMultigraph() {
typeCounts = new TObjectIntHashMap<T>();
vertexToEdges = new TIntObjectHashMap<SparseWeightedDirectedTypedEdgeSet<T>>();
subgraphs = new ArrayList<WeakReference<Subgraph>>();
size = 0;
}
/**
* Creates a directed multigraph with a copy of all the vertices and edges
* in {@code g}.
*/
public WeightedDirectedMultigraph(Graph<? extends WeightedDirectedTypedEdge<T>> g) {
this();
for (Integer v : g.vertices())
add(v);
for (WeightedDirectedTypedEdge<T> e : g.edges())
add(e);
}
/**
* {@inheritDoc}
*/
public boolean add(int vertex) {
if (!vertexToEdges.containsKey(vertex)) {
vertexToEdges.put(vertex, new SparseWeightedDirectedTypedEdgeSet<T>(vertex));
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean add(WeightedDirectedTypedEdge<T> e) {
SparseWeightedDirectedTypedEdgeSet<T> from = vertexToEdges.get(e.from());
if (from == null) {
from = new SparseWeightedDirectedTypedEdgeSet<T>(e.from());
vertexToEdges.put(e.from(), from);
}
if (from.add(e)) {
updateTypeCounts(e.edgeType(), 1);
SparseWeightedDirectedTypedEdgeSet<T> to = vertexToEdges.get(e.to());
if (to == null) {
to = new SparseWeightedDirectedTypedEdgeSet<T>(e.to());
vertexToEdges.put(e.to(), to);
}
to.add(e);
size++;
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public void clear() {
vertexToEdges.clear();
typeCounts.clear();
size = 0;
}
/**
* {@inheritDoc}
*/
public void clearEdges() {
// NOTE: potentially rewrite with a Procedure if this ever needs to be
// optimized
for (SparseWeightedDirectedTypedEdgeSet<T> edges
: vertexToEdges.valueCollection())
edges.clear();
size = 0;
}
/**
* {@inheritDoc}
*/
public void clearEdges(T edgeType) {
throw new Error();
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex) {
return vertexToEdges.containsKey(vertex);
}
/**
* {@inheritDoc}
*/
public boolean contains(Edge e) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(e.to());
return edges != null && edges.contains(e);
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex1, int vertex2) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex1);
return edges != null && edges.connects(vertex2);
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex1, int vertex2, T edgeType) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex2);
return edges != null && edges.connects(vertex2, edgeType);
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> copy(Set<Integer> toCopy) {
// special case for If the called is requesting a copy of the entire
// graph, which is more easily handled with the copy constructor
if (toCopy.size() == order() && toCopy.equals(vertices()))
return new WeightedDirectedMultigraph<T>(this);
WeightedDirectedMultigraph<T> g = new WeightedDirectedMultigraph<T>();
for (int v : toCopy) {
if (!vertexToEdges.containsKey(v))
throw new IllegalArgumentException(
"Request copy of non-present vertex: " + v);
g.add(v);
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(v);
if (edges == null)
throw new IllegalArgumentException();
for (int v2 : toCopy) {
if (v == v2)
break;
if (edges.connects(v2))
for (WeightedDirectedTypedEdge<T> e : edges.getEdges(v2))
g.add(e);
}
}
return g;
}
/**
* {@inheritDoc}
*/
public int degree(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null) ? 0 : edges.size();
}
/**
* Returns the set of typed edges in the graph
*/
public Set<WeightedDirectedTypedEdge<T>> edges() {
return new EdgeView();
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> edges(T t) {
Set<WeightedDirectedTypedEdge<T>> edges =
new HashSet<WeightedDirectedTypedEdge<T>>();
for (SparseWeightedDirectedTypedEdgeSet<T> set :
vertexToEdges.valueCollection()) {
Set<WeightedDirectedTypedEdge<T>> edgesOfType = set.getEdges(t);
if (!edgesOfType.isEmpty())
edges.addAll(edgesOfType);
}
return edges;
}
/**
* Returns the set of edge types currently present in this graph.
*/
public Set<T> edgeTypes() {
return Collections.unmodifiableSet(typeCounts.keySet());
}
/**
* {@inheritDoc}
*/
@Override public boolean equals(Object o) {
if (o instanceof WeightedDirectedMultigraph) {
WeightedDirectedMultigraph<?> dm = (WeightedDirectedMultigraph<?>)(o);
if (dm.typeCounts.equals(typeCounts)) {
return vertexToEdges.equals(dm.vertexToEdges);
}
return false;
}
else if (o instanceof Multigraph) {
@SuppressWarnings("unchecked")
Multigraph<?,TypedEdge<?>> m = (Multigraph<?,TypedEdge<?>>)o;
if (m.edgeTypes().equals(typeCounts.keySet())) {
return m.order() == order()
&& m.size() == size()
&& m.vertices().equals(vertices())
&& m.edges().equals(edges());
}
return false;
}
else if (o instanceof Graph) {
Graph<?> g = (Graph<?>)o;
return g.order() == order()
&& g.size() == size()
&& g.vertices().equals(vertices())
&& g.edges().equals(edges());
}
return false;
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> getAdjacencyList(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: new EdgeListWrapper(edges);
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> getEdges(int vertex1, int vertex2) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex1);
return (edges == null)
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: edges.getEdges(vertex2);
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> getEdges(int vertex1, int vertex2,
Set<T> types) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex1);
return (edges == null)
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: edges.getEdges(vertex2, types);
}
/**
* {@inheritDoc}
*/
public IntSet getNeighbors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: PrimitiveCollections.unmodifiableSet(edges.connected());
}
/**
* {@inheritDoc}
*/
public boolean hasCycles() {
throw new Error();
}
/**
* {@inheritDoc}
*/
public int hashCode() {
return vertexToEdges.keySet().hashCode() ^ (typeCounts.hashCode() * size);
}
/**
* {@inheritDoc}
*/
public int inDegree(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? 0
: edges.predecessors().size();
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> inEdges(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: new EdgeListWrapper(edges.incoming());
}
/**
* {@inheritDoc}
*/
public int order() {
return vertexToEdges.size();
}
/**
* {@inheritDoc}
*/
public int outDegree(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? 0
: edges.successors().size();
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> outEdges(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: new EdgeListWrapper(edges.outgoing());
}
/**
* {@inheritDoc}
*/
public IntSet predecessors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: PrimitiveCollections.unmodifiableSet(edges.predecessors());
}
/**
* {@inheritDoc}
*/
public boolean remove(int vertex) {
// If we can remove the vertex from the global set, then at least one of
// the type-specific graphs has this vertex.
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.remove(vertex);
if (edges != null) {
size -= edges.size();
for (int other : edges.connected()) {
vertexToEdges.get(other).disconnect(vertex);
}
// Check whether removing this vertex has caused us to remove
// the last edge for this type in the graph. If so, the graph
// no longer has this type and we need to update the state.
for (WeightedDirectedTypedEdge<T> e : edges)
updateTypeCounts(e.edgeType(), -1);
// Update any of the subgraphs that had this vertex to notify them
// that it was removed
Iterator<WeakReference<Subgraph>> iter = subgraphs.iterator();
while (iter.hasNext()) {
WeakReference<Subgraph> ref = iter.next();
Subgraph s = ref.get();
// Check whether this subgraph was already gc'd (the subgraph
// was no longer in use) and if so, remove the ref from the list
// to avoid iterating over it again
if (s == null) {
iter.remove();
continue;
}
// If we removed the vertex from the subgraph, then check
// whether we also removed any of the types in that subgraph
if (s.vertexSubset.remove(vertex)) {
Iterator<T> subgraphTypesIter = s.validTypes.iterator();
while (subgraphTypesIter.hasNext()) {
if (!typeCounts.containsKey(subgraphTypesIter.next()))
subgraphTypesIter.remove();
}
}
}
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean remove(WeightedDirectedTypedEdge<T> edge) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(edge.to());
if (edges != null && edges.remove(edge)) {
vertexToEdges.get(edge.from()).remove(edge);
size--;
// Check whether we've just removed the last edge for this type
// in the graph. If so, the graph no longer has this type and
// we need to update the state.
updateTypeCounts(edge.edgeType(), -1);
if (!typeCounts.containsKey(edge.edgeType())) {
// Remove this edge type from all the subgraphs as well
Iterator<WeakReference<Subgraph>> sIt = subgraphs.iterator();
while (sIt.hasNext()) {
WeakReference<Subgraph> ref = sIt.next();
Subgraph s = ref.get();
// Check whether this subgraph was already gc'd (the
// subgraph was no longer in use) and if so, remove the
// ref from the list to avoid iterating over it again
if (s == null) {
sIt.remove();
continue;
}
s.validTypes.remove(edge.edgeType());
}
}
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public int size() {
return size;
}
/**
* {@inheritDoc}
*/
public double strength(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> e = vertexToEdges.get(vertex);
return (e == null) ? 0 : e.sum();
}
/**
* {@inheritDoc}
*/
public IntSet successors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: PrimitiveCollections.unmodifiableSet(edges.successors());
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> subgraph(Set<Integer> subset) {
Subgraph sub = new Subgraph(typeCounts.keySet(), subset);
subgraphs.add(new WeakReference<Subgraph>(sub));
return sub;
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> subgraph(Set<Integer> subset, Set<T> edgeTypes) {
if (edgeTypes.isEmpty())
throw new IllegalArgumentException("Must specify at least one type");
if (!typeCounts.keySet().containsAll(edgeTypes)) {
throw new IllegalArgumentException(
"Cannot create subgraph with more types than exist");
}
Subgraph sub = new Subgraph(edgeTypes, subset);
subgraphs.add(new WeakReference<Subgraph>(sub));
return sub;
}
/**
* Returns a description of the graph as the sequence of its edges.
*/
public String toString() {
// REMINDER: make this more efficient with a StringBuilder
return "{ vertices: " + vertices() + ", edges: " + edges() + "}";
}
/**
* Updates how many edges have this type in the graph
*/
private void updateTypeCounts(T type, int delta) {
if (!typeCounts.containsKey(type)) {
assert delta > 0
: "removing edge type that was not originally present";
typeCounts.put(type, delta);
}
else {
int curCount = typeCounts.get(type);
int newCount = curCount + delta;
assert newCount >= 0
: "removing edge type that was not originally present";
if (newCount == 0)
typeCounts.remove(type);
else
typeCounts.put(type, newCount);
}
}
/**
* {@inheritDoc}
*/
public IntSet vertices() {
return PrimitiveCollections.unmodifiableSet(
TroveIntSet.wrap(vertexToEdges.keySet()));
}
/**
* The {@code EdgeView} class encapsulates all the of the edge sets from the
* different edge types. Because {@link CombinedSet} does not allow for
* addition, we add an {@code add} method that handles adding new edges to
* the graph via this set.
*/
class EdgeView extends AbstractSet<WeightedDirectedTypedEdge<T>> {
public EdgeView() { }
public boolean add(WeightedDirectedTypedEdge<T> e) {
// Calling the backing graph's add will route the edge to the
// type-appropriate set
return WeightedDirectedMultigraph.this.add(e);
}
public boolean contains(Object o) {
return (o instanceof WeightedDirectedTypedEdge)
&& WeightedDirectedMultigraph.this.contains((WeightedDirectedTypedEdge<?>)o);
}
public Iterator<WeightedDirectedTypedEdge<T>> iterator() {
return new EdgeIterator();
}
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
if (o instanceof WeightedDirectedTypedEdge) {
WeightedDirectedTypedEdge<?> e = (WeightedDirectedTypedEdge<?>)o;
return WeightedDirectedMultigraph.this.typeCounts.
containsKey(e.edgeType())
&& WeightedDirectedMultigraph.this.remove((WeightedDirectedTypedEdge<T>)o);
}
return false;
}
public int size() {
return WeightedDirectedMultigraph.this.size();
}
class EdgeIterator implements Iterator<WeightedDirectedTypedEdge<T>> {
Iterator<SparseWeightedDirectedTypedEdgeSet<T>> edgeSets;
Iterator<WeightedDirectedTypedEdge<T>> edges;
WeightedDirectedTypedEdge<T> cur = null;
public EdgeIterator() {
edgeSets = vertexToEdges.valueCollection().iterator();
advance();
}
private void advance() {
while ((edges == null || !edges.hasNext())
&& edgeSets.hasNext()) {
SparseWeightedDirectedTypedEdgeSet<T> edgeSet = edgeSets.next();
edges = edgeSet.uniqueIterator();
}
}
public boolean hasNext() {
return edges != null && edges.hasNext();
}
public WeightedDirectedTypedEdge<T> next() {
if (!hasNext())
throw new NoSuchElementException();
cur = edges.next();
advance();
return cur;
}
public void remove() {
if (cur == null)
throw new IllegalStateException();
WeightedDirectedMultigraph.this.remove(cur);
cur = null;
}
}
}
/**
* A wrapper around a set of of edges that automatically decrements the size
* of this graph when an edge is removed from it.
*
* @see WeightedDirectedMultigraph#inEdges(int)
* @see WeightedDirectedMultigraph#outEdges(int)
*/
class EdgeListWrapper extends SetDecorator<WeightedDirectedTypedEdge<T>> {
private static final long serialVersionUID = 1L;
public EdgeListWrapper(Set<WeightedDirectedTypedEdge<T>> set) {
super(set);
}
@Override public boolean add(WeightedDirectedTypedEdge<T> e) {
return WeightedDirectedMultigraph.this.add(e);
}
@Override public boolean addAll(Collection<? extends WeightedDirectedTypedEdge<T>> c) {
boolean added = false;
for (WeightedDirectedTypedEdge<T> e : c) {
if (WeightedDirectedMultigraph.this.add(e))
added = true;
}
return added;
}
@Override public boolean remove(Object o) {
if (o instanceof WeightedDirectedTypedEdge) {
@SuppressWarnings("unchecked")
WeightedDirectedTypedEdge<T> e = (WeightedDirectedTypedEdge<T>)o;
return WeightedDirectedMultigraph.this.remove(e);
}
return false;
}
@Override public boolean removeAll(Collection<?> c) {
boolean removed = false;
for (Object o : c) {
if (o instanceof WeightedDirectedTypedEdge) {
@SuppressWarnings("unchecked")
WeightedDirectedTypedEdge<T> e = (WeightedDirectedTypedEdge<T>)o;
if (WeightedDirectedMultigraph.this.remove(e))
removed = true;
}
}
return removed;
}
@Override public boolean retainAll(Collection<?> c) {
throw new Error("FIXME");
}
}
/**
* An implementation for handling the subgraph behavior.
*/
class Subgraph extends WeightedDirectedMultigraph<T> {
private static final long serialVersionUID = 1L;
/**
* The set of types in this subgraph
*/
private final Set<T> validTypes;
/**
* The set of vertices in this subgraph
*/
private final IntSet vertexSubset;
public Subgraph(Set<T> validTypes, Set<Integer> vertexSubset) {
this.validTypes = validTypes;
this.vertexSubset = new TroveIntSet(vertexSubset);
}
/**
* {@inheritDoc}
*/
public boolean add(int vertex) {
if (vertexSubset.contains(vertex))
return false;
throw new UnsupportedOperationException(
"Cannot add new vertex to subgraph");
}
/**
* {@inheritDoc}
*/
public boolean add(WeightedDirectedTypedEdge<T> e) {
if (!vertexSubset.contains(e.from())
|| !vertexSubset.contains(e.to())
|| !validTypes.contains(e.edgeType()))
throw new UnsupportedOperationException(
"Cannot add new vertex to subgraph");
return WeightedDirectedMultigraph.this.add(e);
}
/**
* {@inheritDoc}
*/
public void clear() {
for (Integer v : vertexSubset)
WeightedDirectedMultigraph.this.remove(v);
vertexSubset.clear();
}
/**
* {@inheritDoc}
*/
public void clearEdges() {
throw new Error();
}
/**
* {@inheritDoc}
*/
public void clearEdges(T edgeType) {
throw new Error();
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex) {
return vertexSubset.contains(vertex);
}
/**
* {@inheritDoc}
*/
public boolean contains(Edge e) {
if (e instanceof WeightedDirectedTypedEdge) {
WeightedDirectedTypedEdge<?> te = (WeightedDirectedTypedEdge<?>)e;
return vertexSubset.contains(e.from())
&& vertexSubset.contains(e.to())
&& validTypes.contains(te.edgeType())
&& WeightedDirectedMultigraph.this.contains(e);
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex1, int vertex2) {
if (!vertexSubset.contains(vertex1)
|| !vertexSubset.contains(vertex2))
return false;
Set<WeightedDirectedTypedEdge<T>> edges =
WeightedDirectedMultigraph.this.getEdges(vertex1, vertex2);
if (edges != null) {
// If there were edges between the two vertices, ensure that at
// least one of them has the type represented in this subgraph
for (WeightedDirectedTypedEdge<T> e : edges)
if (validTypes.contains(e.edgeType()))
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean contains(int vertex1, int vertex2, T edgeType) {
if (!vertexSubset.contains(vertex1)
|| !vertexSubset.contains(vertex2))
return false;
return WeightedDirectedMultigraph.this.contains(vertex1, vertex2, edgeType);
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> copy(Set<Integer> vertices) {
// special case for If the called is requesting a copy of the entire
// graph, which is more easily handled with the copy constructor
if (vertices.size() == order() && vertices.equals(vertices()))
return new WeightedDirectedMultigraph<T>(this);
WeightedDirectedMultigraph<T> g = new WeightedDirectedMultigraph<T>();
for (int v : vertices) {
if (!contains(v))
throw new IllegalArgumentException(
"Requested copy with non-existant vertex: " + v);
g.add(v);
for (WeightedDirectedTypedEdge<T> e : getAdjacencyList(v)) {
if (vertices.contains(e.from()) && vertices.contains(e.to()))
g.add(e);
}
}
return g;
}
/**
* {@inheritDoc}
*/
public int degree(int vertex) {
return getNeighbors(vertex).size();
}
/**
* Returns the set of typed edges in the graph
*/
public Set<WeightedDirectedTypedEdge<T>> edges() {
return new SubgraphEdgeView();
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> edges(T t) {
throw new Error("implement me");
}
/**
* Returns the set of edge types currently present in this graph.
*/
public Set<T> edgeTypes() {
return Collections.unmodifiableSet(validTypes);
}
/**
* {@inheritDoc}
*/
@Override public boolean equals(Object o) {
if (o instanceof Graph) {
Graph<?> g = (Graph<?>)o;
return g.order() == order()
&& g.size() == size()
&& g.vertices().equals(vertices())
&& g.edges().equals(edges());
}
return false;
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> getAdjacencyList(int vertex) {
if (!vertexSubset.contains(vertex))
return Collections.<WeightedDirectedTypedEdge<T>>emptySet();
Set<WeightedDirectedTypedEdge<T>> adj =
WeightedDirectedMultigraph.this.getAdjacencyList(vertex);
return (adj.isEmpty())
? Collections.<WeightedDirectedTypedEdge<T>>emptySet()
: new SubgraphAdjacencyListView(vertex);
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> getEdges(int vertex1, int vertex2) {
if (!vertexSubset.contains(vertex1)
|| !vertexSubset.contains(vertex2))
return Collections.<WeightedDirectedTypedEdge<T>>emptySet();
Set<WeightedDirectedTypedEdge<T>> edges =
WeightedDirectedMultigraph.this.getEdges(vertex1, vertex2, validTypes);
return edges;
}
/**
* {@inheritDoc}
*/
public IntSet getNeighbors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: new SubgraphNeighborsView(vertex);
}
/**
* {@inheritDoc}
*/
public boolean hasCycles() {
throw new Error();
}
/**
* {@inheritDoc}
*/
public int hashCode() {
return vertices().hashCode() ^ (validTypes.hashCode() * size());
}
/**
* Returns {@code true} if the two vertices are contained within this
* subgraph and have an edge that is valid within this subgraph's type
* constraints.
*/
private boolean hasEdge(int v1, int v2) {
if (vertexSubset.contains(v1) && vertexSubset.contains(v2)) {
Set<WeightedDirectedTypedEdge<T>> edges = getEdges(v1, v2);
return edges != null;
}
return false;
}
/**
* {@inheritDoc}
*/
public int inDegree(int vertex) {
int inDegree = 0;
for (WeightedDirectedTypedEdge<T> e : getAdjacencyList(vertex)) {
if (e.to() == vertex)
inDegree++;
}
return inDegree;
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> inEdges(int vertex) {
// REMINDER: this is probably best wrapped with yet another
// decorator class to avoid the O(n) penality of iteration over all
// the edges
Set<WeightedDirectedTypedEdge<T>> edges = getAdjacencyList(vertex);
if (edges.isEmpty())
return Collections.<WeightedDirectedTypedEdge<T>>emptySet();
Set<WeightedDirectedTypedEdge<T>> in = new HashSet<WeightedDirectedTypedEdge<T>>();
for (WeightedDirectedTypedEdge<T> e : edges) {
if (e.to() == vertex)
in.add(e);
}
return in;
}
/**
* {@inheritDoc}
*/
public int order() {
return vertexSubset.size();
}
/**
* {@inheritDoc}
*/
public int outDegree(int vertex) {
int outDegree = 0;
for (WeightedDirectedTypedEdge<T> e : getAdjacencyList(vertex)) {
if (e.from() == vertex)
outDegree++;
}
return outDegree;
}
/**
* {@inheritDoc}
*/
public Set<WeightedDirectedTypedEdge<T>> outEdges(int vertex) {
// REMINDER: this is probably best wrapped with yet another
// decorator class to avoid the O(n) penality of iteration over all
// the edges
Set<WeightedDirectedTypedEdge<T>> edges = getAdjacencyList(vertex);
if (edges.isEmpty())
return Collections.<WeightedDirectedTypedEdge<T>>emptySet();;
Set<WeightedDirectedTypedEdge<T>> out = new HashSet<WeightedDirectedTypedEdge<T>>();
for (WeightedDirectedTypedEdge<T> e : edges) {
if (e.from() == vertex)
out.add(e);
}
return out;
}
/**
* {@inheritDoc}
*/
public IntSet predecessors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: PrimitiveCollections.unmodifiableSet(edges.predecessors());
}
/**
* {@inheritDoc}
*/
public boolean remove(int vertex) {
throw new UnsupportedOperationException(
"Cannot remove vertices from a subgraph");
}
/**
* {@inheritDoc}
*/
@Override public boolean remove(WeightedDirectedTypedEdge<T> e) {
if (!vertexSubset.contains(e.from())
|| !vertexSubset.contains(e.to())
|| !validTypes.contains(e.edgeType()))
return false;
return WeightedDirectedMultigraph.this.remove(e);
}
/**
* {@inheritDoc}
*/
public int size() {
int size = 0;
System.out.println("types: " + validTypes);
next_vertex:
for (int v1 : vertexSubset) {
for (int v2 : vertexSubset) {
if (v1 == v2)
break;
size += WeightedDirectedMultigraph.this.
getEdges(v1, v2, validTypes).size();
System.out.printf("%d -> %d had %d edges%n", v1, v2,
WeightedDirectedMultigraph.this.
getEdges(v1, v2, validTypes).size());
}
}
return size;
}
/**
* {@inheritDoc}
*/
public IntSet successors(int vertex) {
SparseWeightedDirectedTypedEdgeSet<T> edges = vertexToEdges.get(vertex);
return (edges == null)
? PrimitiveCollections.emptyIntSet()
: PrimitiveCollections.unmodifiableSet(edges.successors());
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> subgraph(Set<Integer> verts) {
if (!vertexSubset.containsAll(verts))
throw new IllegalArgumentException("provided set is not a " +
"subset of the vertices of this graph");
return WeightedDirectedMultigraph.this.subgraph(verts, validTypes);
}
/**
* {@inheritDoc}
*/
public WeightedDirectedMultigraph<T> subgraph(Set<Integer> verts, Set<T> edgeTypes) {
if (!vertexSubset.containsAll(verts))
throw new IllegalArgumentException("provided set is not a " +
"subset of the vertices of this graph");
if (!validTypes.containsAll(edgeTypes))
throw new IllegalArgumentException("provided types is not a " +
"subset of the edge types of this graph");
return WeightedDirectedMultigraph.this.subgraph(verts, edgeTypes);
}
/**
* {@inheritDoc}
*/
public IntSet vertices() {
return PrimitiveCollections.unmodifiableSet(vertexSubset);
}
/**
* A view for the {@code Edge} adjacent edges of a vertex within a
* subgraph. This class monitors for changes to edge set to update the
* state of this graph
*/
private class SubgraphAdjacencyListView
extends AbstractSet<WeightedDirectedTypedEdge<T>> {
private final int root;
public SubgraphAdjacencyListView(int root) {
this.root = root;
}
public boolean add(WeightedDirectedTypedEdge<T> edge) {
return (edge.from() == root
|| edge.to() == root)
&& Subgraph.this.add(edge);
}
public boolean contains(Object o) {
if (!(o instanceof Edge))
return false;
Edge e = (Edge)o;
return (e.to() == root ||
e.from() == root)
&& Subgraph.this.contains(e);
}
public Iterator<WeightedDirectedTypedEdge<T>> iterator() {
return new SubgraphAdjacencyListIterator();
}
public boolean remove(Object o) {
if (!(o instanceof WeightedDirectedTypedEdge))
return false;
WeightedDirectedTypedEdge<?> e = (WeightedDirectedTypedEdge<?>)o;
if (!validTypes.contains(e.edgeType()))
return false;
@SuppressWarnings("unchecked")
WeightedDirectedTypedEdge<T> e2 = (WeightedDirectedTypedEdge<T>)o;
return (e2.to() == root ||
e2.from() == root)
&& Subgraph.this.remove(e2);
}
public int size() {
int sz = 0;
for (Integer i : vertexSubset)
sz += getEdges(root, i).size();
return sz;
}
/**
* A decorator around the iterator for the adjacency list for a
* vertex in a subgraph, which tracks edges removal to update the
* number of edges in the graph.
*/
private class SubgraphAdjacencyListIterator
implements Iterator<WeightedDirectedTypedEdge<T>> {
Iterator<WeightedDirectedTypedEdge<T>> edges;
public SubgraphAdjacencyListIterator() {
throw new Error();
}
public boolean hasNext() {
return edges.hasNext();
}
public WeightedDirectedTypedEdge<T> next() {
return edges.next();
}
public void remove() {
edges.remove();
}
}
}
/**
* A view-based class over the edges in a {@link Subgraph}. This class
* masks the state of the edges that are in the backing graph but not in
* the subgraph.
*/
private class SubgraphEdgeView extends AbstractSet<WeightedDirectedTypedEdge<T>> {
public SubgraphEdgeView() { }
/**
* Throws an {@link UnsupportedOperationException} if called.
*/
public boolean add(WeightedDirectedTypedEdge<T> e) {
return Subgraph.this.add(e);
}
public boolean contains(Object o) {
if (!(o instanceof WeightedDirectedTypedEdge))
return false;
@SuppressWarnings("unchecked")
WeightedDirectedTypedEdge<T> edge = (WeightedDirectedTypedEdge<T>)o;
return Subgraph.this.contains(edge);
}
public Iterator<WeightedDirectedTypedEdge<T>> iterator() {
return new SubgraphEdgeIterator();
}
/**
* {@inheritDoc}
*/
public boolean remove(Object o) {
if (!(o instanceof WeightedDirectedTypedEdge))
return false;
@SuppressWarnings("unchecked")
WeightedDirectedTypedEdge<T> edge = (WeightedDirectedTypedEdge<T>)o;
return Subgraph.this.remove(edge);
}
public int size() {
return Subgraph.this.size();
}
/**
* An iterator over the edges in this subgraph, which uses the
* pair-wise combinations of edges to identify which edges should be
* returned.
*/
private class SubgraphEdgeIterator
implements Iterator<WeightedDirectedTypedEdge<T>> {
private final Queue<WeightedDirectedTypedEdge<T>> edgesToReturn;
private final Queue<Integer> remainingVertices;
private Iterator<Integer> possibleNeighbors;
private Integer curVertex;
public SubgraphEdgeIterator() {
remainingVertices =
new ArrayDeque<Integer>(Subgraph.this.vertices());
edgesToReturn = new ArrayDeque<WeightedDirectedTypedEdge<T>>();
curVertex = null;
advance();
}
private void advance() {
// If there are still edges from the current pair, skip
// processing any further connections
if (!edgesToReturn.isEmpty())
return;
do {
// Find the next vertex to analyze for connections
while ((possibleNeighbors == null
|| !possibleNeighbors.hasNext())
&& !remainingVertices.isEmpty()) {
// Pull off the next vertex and then use the remaining
// vertices to check for connections
curVertex = remainingVertices.poll();
possibleNeighbors = remainingVertices.iterator();
}
// Iterate through the possible neighbors of this
// vertex, stopping when we find another vertex that has
// at least one edge
while (edgesToReturn.isEmpty()
&& possibleNeighbors.hasNext()) {
Integer v = possibleNeighbors.next();
edgesToReturn.addAll(getEdges(curVertex, v));
}
} while (edgesToReturn.isEmpty()
&& !remainingVertices.isEmpty());
}
public boolean hasNext() {
return edgesToReturn.size() > 0;
}
public WeightedDirectedTypedEdge<T> next() {
if (!hasNext())
throw new NoSuchElementException();
WeightedDirectedTypedEdge<T> n = edgesToReturn.poll();
advance();
return n;
}
public void remove() {
throw new IllegalStateException();
}
}
}
/**
* A view of a subgraph's vertex's neighbors that are also in the
* subview. This view monitors for additions and removals to the set in
* order to update the state of this {@code Subgraph}.
*/
private class SubgraphNeighborsView extends AbstractIntSet {
private int root;
/**
* Constructs a view around the set of adjacent vertices
*/
public SubgraphNeighborsView(int root) {
this.root = root;
}
public boolean add(int vertex) {
throw new UnsupportedOperationException(
"Cannot add vertices to subgraph");
}
public boolean add(Integer vertex) {
throw new UnsupportedOperationException(
"Cannot add vertices to subgraph");
}
public boolean contains(int vertex) {
return vertexSubset.contains(vertex)
&& isNeighboringVertex(vertex);
}
public boolean contains(Object o) {
if (!(o instanceof Integer))
return false;
Integer i = (Integer)o;
return vertexSubset.contains(i) && isNeighboringVertex(i);
}
private boolean isNeighboringVertex(Integer i) {
return Subgraph.this.contains(root, i);
}
public IntIterator iterator() {
return new SubgraphNeighborsIterator();
}
public boolean remove(int vertex) {
throw new UnsupportedOperationException(
"Cannot remove vertices from subgraph");
}
public boolean remove(Object vertex) {
throw new UnsupportedOperationException(
"Cannot remove vertices from subgraph");
}
public int size() {
int sz = 0;
for (Integer v : vertexSubset) {
if (isNeighboringVertex(v))
sz++;
}
return sz;
}
/**
* A decorator around the iterator for a subgraphs's neighboring
* vertices set, which keeps track of which neighboring vertices are
* actually in this subview.
*/
private class SubgraphNeighborsIterator implements IntIterator {
private final Iterator<Integer> iter;
private Integer next;
public SubgraphNeighborsIterator() {
iter = vertexSubset.iterator();
advance();
}
/**
* Finds the next adjacent vertex that is also in this subview.
*/
private void advance() {
next = null;
while (iter.hasNext() && next == null) {
Integer v = iter.next();
if (isNeighboringVertex(v))
next = v;
}
}
public boolean hasNext() {
return next != null;
}
public Integer next() {
if (!hasNext())
throw new NoSuchElementException();
Integer cur = next;
advance();
return cur;
}
public int nextInt() {
return next();
}
/**
* Throws an {@link UnsupportedOperationException} if called.
*/
public void remove() {
throw new UnsupportedOperationException();
}
}
}
}
}