package net.sf.egonet.network;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.sf.functionalj.tuple.PairUni;
public class Network<N>
{
private Set<N> nodes;
private Set<PairUni<N>> edges;
private Map<N,Set<N>> nodeToConnections;
private Map<N,Map<N,Integer>> distanceBetweenNodes;
public Network(Set<N> nodes, Set<PairUni<N>> edges)
{
ensureEdgeNodesAreIn(nodes, edges);
this.nodes = Collections.unmodifiableSet(nodes);
this.edges = Collections.unmodifiableSet(edges);
Multimap<N,N> directConnections = ArrayListMultimap.create();
for(PairUni<N> edge : edges) {
directConnections.put(edge.getFirst(), edge.getSecond());
directConnections.put(edge.getSecond(), edge.getFirst());
}
this.nodeToConnections = Maps.newHashMap();
for(N node : nodes) {
this.nodeToConnections.put(node, Collections.unmodifiableSet(new HashSet<N>(directConnections.get(node))));
}
distanceBetweenNodes = Maps.newHashMap();
for(N startingPoint : nodes) {
Map<N,Integer> distanceToNode = Maps.newHashMap();
Set<N> frontier = Sets.newHashSet();
frontier.add(startingPoint);
Integer distance = 0;
while(! frontier.isEmpty()) {
Set<N> newFrontier = Sets.newHashSet();
for(N frontierNode : frontier) {
if(! distanceToNode.containsKey(frontierNode)) {
distanceToNode.put(frontierNode, distance);
for(N newFrontierNode : connections(frontierNode)) {
if(! distanceToNode.containsKey(newFrontierNode)) {
newFrontier.add(newFrontierNode);
}
}
}
}
frontier = newFrontier;
distance++;
}
distanceBetweenNodes.put(startingPoint, distanceToNode);
}
}
private void ensureEdgeNodesAreIn(Set<N> nodes, Set<PairUni<N>> edges)
{
// all edges should have nodes that are in nodes
for (PairUni<N> edge : edges)
{
Preconditions.checkArgument(nodes.contains(edge.getFirst()));
Preconditions.checkArgument(nodes.contains(edge.getSecond()));
}
}
public Set<N> getNodes() { return this.nodes; }
public Set<PairUni<N>> getEdges() { return this.edges; }
public Set<N> connections(N node) {
return this.nodeToConnections.get(node);
}
// Returns null if there is no connection.
public Integer distance(N node1, N node2) {
return distanceBetweenNodes.get(node1).get(node2);
}
}